
// ToolsLib Project

/* ToolsLib library for RusRoute firewall and other projects of
 * Andrey A. Moiseenko / IE Moiseenko A.A. (Russia)
 * e-mail: support@maasoftware.ru, maa2002@mail.ru
 * web: http://maasoftware.ru, http://maasoftware.com, http://maasoft.ru, http://maasoft.org
 * Author's full name: Andrey Alekseevitch Moiseenko
 * (russian name: Моисеенко Андрей Алексеевич)
 */

// ToolsLib/StringEx.h

/* Copyright (C) 2002-2024 Andrey A. Moiseenko (support@maasoftware.ru)
 * All rights reserved.
 *
 * This library contains strings and string's functions implementation.
 * This CMaaString does not throws throws exception out of boundaries.
 * The library implementation written
 * by Andrey A. Moiseenko (support@maasoftware.ru).
 * This library and applications are
 * FREE FOR COMMERCIAL AND NON-COMMERCIAL USE
 * as long as the following conditions are aheared to.
 *
 * Copyright remains Andrey A. Moiseenko, and as such any Copyright notices in
 * the code are not to be removed.  If this code is used in a product,
 * Andrey A. Moiseenko should be given attribution as the author of the parts used.
 * This can be in the form of a textual message at program startup or
 * in documentation (online or textual) provided with the package.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Andrey A. Moiseenko (support@maasoftware.ru)
 *
 * THIS SOFTWARE IS PROVIDED BY ANDREY A. MOISEENKO ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * The licence and distribution terms for any publically available version or
 * derivative of this code cannot be changed.  i.e. this code cannot simply be
 * copied and put under another distribution licence
 * [including the GNU Public Licence.]
 */

#if !defined(_WIN32) && !defined(__unix__)
     #ifndef __LARGE__
          #error Tools ( CMaaString::Format ) needs the large memory model
     #endif
#endif

#define CMAASTRING_CACHE_HASH // experimental, has effects: need to call ClearHash() on buffer direct mod
//#define MANUAL_STRINGS_REFS false
#define MANUAL_STRINGS_REFS true

typedef int warning_int;
inline int int_strlen(const char* text) noexcept { return (warning_int)strlen(text); } // warning size mismatch
#ifdef TOOLSLIB_CHAR8T_SUPPORT
inline int int_strlen(const char8_t* text) noexcept { return (warning_int)strlen((const char *)text); } // warning size mismatch
#endif

//------------------------------------------------------------------------------
//#define USE_FIND_KMP // use KMP is slower!!
//------------------------------------------------------------------------------

typedef int* pINT_CMaaTL;
typedef const void* pcVOID_CMaaTL;
typedef const char* pcCHAR_CMaaTL;

bool mysscanf64(const char * str, _qword * ptr, int Len = -1, int * StopPos = nullptr, const char * StopChars = nullptr) noexcept;
bool mysscanfu64(const char * str, _uqword * ptr, int Len = -1, int * StopPos = nullptr, const char * StopChars = nullptr) noexcept;
bool CMaaIsMemEq08(const char* p1, const char* p2, int count) noexcept; // for count == 0..8, false for count < 0
bool CMaaIsMemEq0_16(const char* p1, const char* p2, int count) noexcept; // for count == 0..16, false for count < 0
bool MaaMemCmpQ_C(const _qword* ptr1, const _qword* ptr2, int count) noexcept; // compares len >= 0 ptr1, ptr2; len < 0 return false

#ifdef TOOLSLIB_ASM64
//__utf8_printf("           alloc&fill(0),   ==,       ==\n");
//__utf8_printf("_WIN64 2000000000 bytes compare:\n");
//__utf8_printf("!memcmp:        291,528,  152,631,  152,113, 1, 0\n");
//__utf8_printf("MaaMemCmpQ_C:   294,455,  151,547,  153,506, 1, 0\n");
//__utf8_printf("MaaMemCmpQ_win: 293,042,  150,073,  151,194, 1, 0\n");
//__utf8_printf("_WIN32 2000000000/3 bytes compare:\n");
//__utf8_printf("!memcmp:        101,923,  80,951,  80,210, 1, 0\n");
//__utf8_printf("MaaMemCmpQ_C:   99,424,  64,218,  63,640, 1, 0\n");
//__utf8_printf("__linux__ (64) 2000000000 bytes compare:\n");
//__utf8_printf("!memcmp:        222,304,  135,243,  135,181, 1, 0\n");
//__utf8_printf("MaaMemCmpQ_C:   220,938,  155,321,  155,206, 1, 0\n");
//__utf8_printf("MaaMemCmpQ_win: 220,809,  159,939,  159,709, 1, 0\n");

#ifdef _WIN64________THE_SAME // this asm function is the same performance
extern "C"
{
    bool MaaMemCmpQ_win(const void* ptr1, const void* ptr2, _uqword len) noexcept; // compares len ptr1, ptr2, len >= 0
}
#define MaaMemCmpQ MaaMemCmpQ_win
#else
//
#endif

#ifdef __linux__a________SLOW
extern "C"
{
    bool MaaMemCmpQ_unix(const void* ptr1, const void* ptr2, _uqword len) noexcept; // compares len ptr1, ptr2, len >= 0
}
#define MaaMemCmpQ MaaMemCmpQ_unix
#endif

#endif

#ifdef MaaMemCmpQ
#define CMaaIsMemEq MaaMemCmpQ
#else
#ifdef __linux__
inline bool CMaaIsMemEq(const void* ptr1, const void* ptr2, int count) noexcept // fast __linux__ memcmp
{
    return !memcmp(ptr1, ptr2, count); // fastest
    //return MaaMemCmpQ_C((const _qword*)ptr1, (const _qword*)ptr2, count);
}
#else
inline bool CMaaIsMemEq(const void* ptr1, const void* ptr2, int count) noexcept // fast memcmp, use only count >= 0 for compatibility with !memcmp(ptr1,ptr2,count)
{
#ifdef _WIN64__________
    return !memcmp(ptr1, ptr2, count); // slower
#else
    return MaaMemCmpQ_C((const _qword*)ptr1, (const _qword*)ptr2, count);
#endif
}
#endif
#endif

class ToolsExport CMaaString;
//--------------------------------------------------------------------------
//CMaaString DataToString ( const void * ptr, int len );
//--------------------------------------------------------------------------

//extern CMaaLiteMutex * gpCMaaSyncAssignLock;
#ifndef TOOLSLIB_SINGLE_THREAD
void gCMaaSyncAssignLockUpperLock() noexcept;
void gCMaaSyncAssignLockUpperUnLock() noexcept;
#endif

enum eCodePageConvert
{
    e_utf8 = 65001,
    e_utf8_rus = 65001,
    e_string_depending = -2,
    e_string_depending_rus = -2,
    e_string_depending_all = e_string_depending_rus
};

template<class T> void CMaaSyncAssign(T &a, const T &b, bool bInc, bool bDel = false) noexcept( noexcept(T(a)) && noexcept(a=b) )
{
#ifndef TOOLSLIB_SINGLE_THREAD
    gCMaaSyncAssignLockUpperLock();
    //gCMaaSyncAssignLock.Lock();
#endif
    if  (b && bInc)
    {
        ((T &)b)->AddRef();
    }
    T tmp = a;
    a = b;
    if  (tmp)
    {
        if  (!tmp->Release() && bDel)
        {
            delete tmp;
        }
    }
#ifndef TOOLSLIB_SINGLE_THREAD
    gCMaaSyncAssignLockUpperUnLock();
    //gCMaaSyncAssignLock.UnLock();
#endif
}

template<class T> void CMaaSyncAssignFast(T &a, const T &b, bool bInc, bool bDel = false) noexcept( noexcept(T(a)) && noexcept(a=b) )
{
    //gCMaaSyncAssignLock.Lock();
    if  (b && bInc)
    {
        ((T &)b)->AddRef();
    }
    T tmp = a;
    a = b;
    if  (tmp)
    {
        if  (!tmp->Release() && bDel)
        {
            delete tmp;
        }
    }
    //gCMaaSyncAssignLock.UnLock();
}

//void CMaaSyncAssign<CMaaString::CMaaStringImp *>(CMaaString::CMaaStringImp * &a, const CMaaString::CMaaStringImp * &b, bool bInc);

class CMaaString;
CMaaString UnicodeString(const _WC_ * pText, int Len = -1, int AddRemLastCh = 0, bool bMemoryString = false);
CMaaString UnicodeToUtf8(const CMaaString &Text, int AddRemLastCh = 0);
CMaaString UnicodeToUtf8(const _WC_ * pText, int Len = -1, int AddRemLastCh = 0);
//CMaaString UnicodeToUtf8(const _WC_ * pText, int Len, bool RemoveLastNullChar = true);

CMaaString Unicode32String(const char32_t * pText, int Len = -1, int AddRemLastCh = 0, bool bMemoryString = false);
CMaaString Unicode32ToUtf8(const CMaaString &Text, int AddRemLastCh = 0);
CMaaString Unicode32ToUtf8(const char32_t * pText, int Len = -1, int AddRemLastCh = 0);
//CMaaString Unicode32ToUtf8(const char32_t * pText, bool RemoveLastNullChar = true);

// Linux: sizeof(wchar_t) == 4;  Windows: sizeof(wchar_t) == 2.
// Use char16_t *  ' _WC_ * str = u"\u4e66\u4e2d\u81ea\u6709\u9ec4\u91d1\u5c4b\u0000\u0000"; ',
// not          ' wchar_t * str = U"\U0001F37A\n"; ' for compatible strings
CMaaString wchar_t_ToUtf8(const wchar_t * pText, int Len = -1, int AddRemLastCh = 0);

CMaaString Utf8ToUnicode(const CMaaString &Text, int AddRemLastCh = 0);
CMaaString Utf8ToUnicode32(const CMaaString &Text, int AddRemLastCh = 0);
CMaaString UnicodeToUnicodeBigEndian(const CMaaString &Text); // 16
CMaaString UnicodeBigEndianToUnicode(const CMaaString &Text); // 16
//------------------------------------------------------------------------------
#define CMaaStringZ CMaaString::sCMaaStringZ
#define CMaaString0 CMaaString::sCMaaString0
#define CMaaString00 CMaaString::sCMaaString00
#define CMaaStringWC0 CMaaString::sCMaaStringWC0
#define CMaaStringWChar0 CMaaString::sCMaaStringWChar0
#define CMaaStringWC32_0 CMaaString::sCMaaStringWC32_0
#define CMaaStringEta CMaaString::sCMaaStringEta
#define CMaaStringSp CMaaString::sCMaaStringSp
#define CMaaString_0 CMaaString::sCMaaString_0
#define CMaaString_a CMaaString::sCMaaString_a
#define CMaaString_x CMaaString::sCMaaString_x
#define CMaaStringCr CMaaString::sCMaaStringCr
#define CMaaStringLf CMaaString::sCMaaStringLf
#define CMaaStringCrLf CMaaString::sCMaaStringCrLf
#define CMaaStringSpSp CMaaString::sCMaaStringSpSp
#define CMaaStringJsonVal CMaaString::sCMaaStringJsonVal
#define CMaaStringJsonNode CMaaString::sCMaaStringJsonNode
#define CMaaStringJsonArray CMaaString::sCMaaStringJsonArray
#define CMaaStringJsonName CMaaString::sCMaaStringJsonName
#define CMaaStringJsonValue CMaaString::sCMaaStringJsonValue
#define CMaaStringJsonType CMaaString::sCMaaStringJsonType
#define CMaaStringJsonInt CMaaString::sCMaaStringJsonInt
#define CMaaStringJsonBool CMaaString::sCMaaStringJsonBool
#define CMaaStringJsonFloat CMaaString::sCMaaStringJsonFloat
#define CMaaStringJsonNull CMaaString::sCMaaStringJsonNull
#define CMaaStringJsonString CMaaString::sCMaaStringJsonString

/*
#define CMaaString_Nums CMaaString::sCMaaString_Nums
#define CMaaString_0 CMaaString::sCMaaString_Nums[0]
#define CMaaString_1 CMaaString::sCMaaString_Nums[1]
#define CMaaString_2 CMaaString::sCMaaString_Nums[2]
#define CMaaString_3 CMaaString::sCMaaString_Nums[3]
#define CMaaString_4 CMaaString::sCMaaString_Nums[4]
#define CMaaString_5 CMaaString::sCMaaString_Nums[5]
#define CMaaString_6 CMaaString::sCMaaString_Nums[6]
#define CMaaString_7 CMaaString::sCMaaString_Nums[7]
#define CMaaString_8 CMaaString::sCMaaString_Nums[8]
#define CMaaString_9 CMaaString::sCMaaString_Nums[9]
#define CMaaString_a CMaaString::sCMaaString_a
#define CMaaString_z CMaaString::sCMaaString_z
#define CMaaString_A CMaaString::sCMaaString_A
#define CMaaString_Z CMaaString::sCMaaString_Z
*/
#define CMaaStringSemicolon CMaaString::sCMaaStringSemicolon
#define CMaaStringComma CMaaString::sCMaaStringComma
#define CMaaStringDot CMaaString::sCMaaStringDot
#define CMaaStringDotDot CMaaString::sCMaaStringDotDot
//#define CMaaStringEllipse CMaaString::sCMaaStringEllipse
#define CMaaStringDoc CMaaString::sCMaaStringDoc
bool IsCi(const char* s1, const char* s2, int Len, int cp = -1 /*default: +rus*/) noexcept; // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
bool IsCi0(const char* s1, const char* s2, int Len1, int cp = -1 /*default: +rus*/, bool bIsLeft1 = false) noexcept; // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
bool IsCi00(const char* s1, const char* s2, int cp = -1 /*default: +rus*/) noexcept; // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
//------------------------------------------------------------------------------
extern const CMaa256Bits gSpacesBits; //, gSpacesBitsComma, gSpacesBitsColon, gSpacesBitsSemicolon, gSpacesBitsSpace, gSpacesBitsDot; // (" \t", 2); (",", 1); (":", 1); (";", 1); (" ", 1); (".", 1);
//------------------------------------------------------------------------------
// class CMaaString
//------------------------------------------------------------------------------
class ToolsExport CMaaString
{
    static const char sChar0_1[8], sChar0_2[8], sChar0_3[10];
    static CMaaString s_szSpaces;
public:
    // return static error string
    static CMaaString GetStaticErrorString(int nerr) noexcept;

    // return text representation of ptr memory of len bytes
    static CMaaString DataToString(const void* ptr, int len);

    //CMaaString() noexcept;
    CMaaString() noexcept : m_pImp(nullptr) {}
    CMaaString(const char* pszStr);
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaString(const char8_t* pszStr);
#endif

    // copy of pMem or Len bytes zeroes if !pMem
    CMaaString(const void* pMem, size_t Len);

    enum _e1_
    {
        eNotInitMem //= -1,//
        //e1_ = eNotInitMem
    };
    enum _e0
    {
        eMemString = 0, //= 0,

        // for
        //eROString     =    8,
        //eLengthLimit1 = 0x10,
        //eLengthLimit2 = 0x20,
        //eLengthLimit4 = 0x40,
        // :

        eMemString0     =               0x10,
        eMemString00    =        0x20 | 0x10,
        eMemString0000  = 0x40 | 0x20 | 0x10,

        eROMemString     = 8 |                  0,
        eROMemString0    = 8 |               0x10,
        eROMemString00   = 8 |        0x20 | 0x10,
        eROMemString0000 = 8 | 0x40 | 0x20 | 0x10

        //e0 = eMemString
    };
    enum _e1
    {
        eStrlenMemString //= 0,
        //eROStrlenMemString = 8,
        //e1 = eStrlenMemString
    };
    enum _e2
    {
        //eStrlenMemString = 0,
        eROStrlenMemString //= 8,
    };

    // CMaaString(nullptr, Len, -1) allocates Len bytes non-initialized memory, or direct memory referenced string if ptr != nullptr Len bytes
    // CMaaString(ptr, >0, 1) - CMaaString(ptr, 1, 1) creates direct memory referenced string strlen(ptr) size, noexcept: m_pImp=nullptr on New error
    // CMaaString(ptr, Len>0, (Flags!=-1||ptr) && Flags!=1) - CMaaString(ptr, Len, 0) creates direct memory referenced string Len size, noexcept: m_pImp=nullptr on New error
    CMaaString(const void* pMem, size_t Len, int Flags /**/) = delete; // need "int constexpr Flags" parameter

    // CMaaString(nullptr, Len, CMaaString::eNotInitMem) allocates Len bytes non-initialized memory, or (temp!) direct memory referenced string if ptr != nullptr Len bytes
    // CMaaString(ptr, Len>0, CMaaString::eMemString) - CMaaString(ptr, Len, CMaaString::eMemString) creates direct memory referenced string Len size, noexcept, m_pImp=nullptr on (New error || Len <= 0)
    // CMaaString(ptr, any, CMaaString::eStrlenMemString) - CMaaString(ptr, 1, CMaaString::eStrlenMemString) creates direct memory referenced string strlen(ptr) size, noexcept, m_pImp=nullptr on (New error || Len <= 0)
    CMaaString(const void* pMem, size_t Len, _e1_ Flags /**/);
    CMaaString(const void* pMem, size_t Len, _e0 Flags /**/) noexcept;
    CMaaString(const void* pMem, size_t Len, _e1 Flags /**/) noexcept = delete;
    CMaaString(const void* pMem, size_t Len, _e2 Flags /**/) noexcept = delete;
    CMaaString(const void* pMem, _e1 Flags /**/) noexcept;
    CMaaString(const void* pMem, _e2 Flags /**/) noexcept;

    // CMaaString(nullptr, Len, -1) allocates Len bytes non-initialized memory, or direct memory referenced string if ptr != nullptr Len bytes
    // CMaaString(ptr, >0, 1) - CMaaString(ptr, 1, 1) creates direct memory referenced string strlen(ptr) size, noexcept: m_pImp=nullptr on New error
    // CMaaString(ptr, Len>0, (Flags!=-1||ptr) && Flags!=1) - CMaaString(ptr, Len, 0) creates direct memory referenced string Len size, noexcept: m_pImp=nullptr on New error
    CMaaString(const void* pMem, int Len, int Flags /**/) = delete; // need "int constexpr Flags" parameter

    // CMaaString(nullptr, Len, CMaaString::eNotInitMem) allocates Len bytes non-initialized memory, or (temp!) direct memory referenced string if ptr != nullptr Len bytes
    // CMaaString(ptr, Len>0, CMaaString::eMemString) - CMaaString(ptr, Len, CMaaString::eMemString) creates direct memory referenced string Len size, noexcept, m_pImp=nullptr on (New error || Len <= 0)
    // CMaaString(ptr, any, CMaaString::eStrlenMemString) - CMaaString(ptr, 1, CMaaString::eStrlenMemString) creates direct memory referenced string strlen(ptr) size, noexcept, m_pImp=nullptr on (New error || Len <= 0)
    CMaaString(const void* pMem, int Len, _e1_ Flags /**/);
    CMaaString(const void* pMem, int Len, _e0 Flags /**/) noexcept;
    CMaaString(const void* pMem, int Len, _e1 Flags /**/) noexcept = delete;
    CMaaString(const void* pMem, int Len, _e2 Flags /**/) noexcept = delete;

    CMaaString(const CMaaString& That, size_t Start, size_t Len) noexcept;
    CMaaString(const CMaaString& That, int Start, int Len) noexcept;
    CMaaString(const CMaaString& That) noexcept;
    CMaaString(CMaaString&& That) noexcept;
    CMaaString& operator=(const CMaaString& That) noexcept;
    CMaaString& operator=(CMaaString&& That) noexcept;
    CMaaString& operator=(const char* pszTxt);
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaString& operator=(const char8_t* pszTxt);
#endif

    CMaaString& operator=(const CMaaString& That) const noexcept = delete;
    CMaaString& operator=(CMaaString&& That) const noexcept = delete;
    CMaaString& operator=(const char* pszTxt) const = delete;
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaString& operator=(const char8_t* pszTxt) const = delete;
#endif

    bool IsEmpty() const noexcept { return !m_pImp; /*!Length();*/ }
    bool IsNotEmpty() const noexcept { return m_pImp; /*Length();*/ }

    // inline fn:
    // 
    ~CMaaString() noexcept;
    operator const char* () const noexcept;
    //operator const char8_t* () const noexcept;
    //char* GetBuffer() noexcept;
    //int Length() const noexcept;
    //int __CharCount() const noexcept;
    //int __Pos(int CharIndex) const noexcept;
    //CMaaString __Left(int CharIndex);
    //CMaaString __Ellipse(int CharIndex = 64, CMaaString el = CMaaStringEllipse);
    int LastChar() const noexcept; // -0x100 if length == 0, else (int)(char)[..]
    int LastUChar() const noexcept; // -0x100 if length == 0, else (int)(unsigned char)(char)[..]
    ////CMaaString operator += (char c);
    //bool IsLeft(const char * ptr, int nCount) const noexcept;
    //bool IsRight(const char * ptr, int nCount) const noexcept;
    //bool IsMid(const char * ptr, int nFirst, int nCount) const noexcept;
    _dword Hash() const noexcept;
    //int GetLength() const noexcept;
    void ClearHash() noexcept; // call just before before/after direct string modifications
    void Empty() noexcept;
    //bool Is0Behind() const noexcept;
    //bool IsWC0Behind() const noexcept;
    //bool IsWChar0Behind() const noexcept;
    //bool IsWC32_0Behind() const noexcept;

    // alloc copy of string
    CMaaString NewCopy() const
    {
        return CMaaString((const char*)*this, Length());
    }
    CMaaString Str0Copy() const
    {
        return Is0Behind() ? *this : NewCopy();
    }

#ifdef TOOLSLIB_CHAR8T_SUPPORT
    bool operator != (const char8_t* str) const noexcept;
#endif
    bool operator != (const char* str) const noexcept;
    bool operator == (const char* str) const noexcept;
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    bool operator == (const char8_t* str) const noexcept;
#endif
    bool operator != (const CMaaString& That) const noexcept;
    bool operator == (const CMaaString& That) const noexcept;
    bool StrictEq(const CMaaString& That) const noexcept // ===
    {
        return m_pImp == That.m_pImp;
    }
    /*
    bool EqEx(const CMaaString& That) const noexcept
    {
        return m_pImp == That.m_pImp || *this == That;
    }
    */
    bool operator < (const CMaaString& That) const noexcept;
    bool operator > (const CMaaString& That) const noexcept;
    bool operator <= (const CMaaString& That) const noexcept;
    bool operator >= (const CMaaString& That) const noexcept;

    static int Compare(const CMaaString& a, const CMaaString& b) noexcept;
    static int Compare(const CMaaString& a, const CMaaString& b, int Type /*1 - unicode, 2 - utf8, 3 - utf8 with qSortOptimmizationWithStringInjection*/);

    char* GetBuffer() noexcept
    {
        ClearHash();
        return (char*)(const char*)(*this);
    }
    static bool IsBadStrPtr(CMaaString *p); // check is the string memory readable/writable

    //char operator [] ( size_t Index ) const;

    int Length() const noexcept
    {
        return m_pImp ? m_pImp->m_iLength : 0;
    }
    //int Utf8CharCount() const {return Utf8CharCount(true);}
    int Utf8CharCount(bool CutAsFull = true) const noexcept;
    _sdword Utf8CharsSequenceToUnicodeChar(int &pos, bool bMovePos = false, bool bAllowExtendedWideChars = true) noexcept;
    CMaaString Utf8ToPrintable(bool bAllowCrLf = true); // characters range < ' ' --> '_'
    //int Utf8Pos(int CharIndex) const {return Utf8Pos(CharIndex, true);}
    int Utf8Pos(int CharIndex, bool CutAsFull = true) const noexcept;
    int Utf8PosFromPos0(int CharIndex, int pos0 = 0, bool CutAsFull = true) const noexcept; // pos0 - bytes pos. pos0=0 === Utf8Pos
    int Utf8CharsLenFromPos0(int CharsLen, int pos0 = 0, bool bRetErrors = true, bool bUtf32 = false) const noexcept; // pos0 - bytes pos. returts len of CharsLen chars in bytes, -1 on errors && bRetErrors, -4 on utf32 char && !bUtf32
    int __CharCount() const noexcept
    {
#if defined(_UNICODE) || defined(__unix__)
        return Utf8CharCount();
#else
        return Length();
#endif
    }
    int __Pos(int CharIndex) const noexcept
    {
#if defined(_UNICODE) || defined(__unix__)
        return Utf8Pos(CharIndex);
#else
        return CharIndex < 0 ? 0 : CharIndex < Length() ? CharIndex : Length();
#endif
    }
    CMaaString __Left(int CharIndex)
    {
        return Left(__Pos(CharIndex));
    }
    CMaaString __Ellipse(int CharIndex = 64, CMaaString el = "...") // = CMaaStringEllipse
    {
        CMaaString r = __Left(CharIndex);
        if  (r.Length() != Length())
        {
            r += el;
        }
        return r;
    }

    CMaaString RemoveFromRight(int nCount) const;

    bool IsValid() const noexcept
    {
        return m_pImp != nullptr;
    }

    bool isEmpty() const noexcept
    {
        return !m_pImp; // Length() == 0; // or !m_pImp
    }

    //CMaaString operator + ( const char * pszStr );
    //CMaaString & operator += ( const char * pszStr );
    CMaaString & operator += (const CMaaString &That);
    CMaaString & Add(const void * pMem, int Len); // slow
    CMaaString & operator += (const char * szString);
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaString & operator += (const char8_t * szString);
#endif
    CMaaString & operator += (char c) { return Add(&c, 1); }
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaString & operator += (char8_t c) { return Add(&c, 1); }
#endif
    //CMaaString & operator += (_WC_ c) { return Add(&c, (int)sizeof(c)); }
    //CMaaString & operator += (char32_t c) { return Add(&c, (int)sizeof(c)); }
    //CMaaString & operator += (wchar_t c) { return Add(&c, (int)sizeof(c)); }
    CMaaString operator + (const char * szString) const;
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaString operator + (const char8_t * szString) const;
#endif
    CMaaString operator + (const CMaaString & That) const;
    char at(int idx) {return (char)(*this)[idx];} // temp fn, not use it
    int length() const noexcept {return (*this).Length(); } // temp fn, not use it

    static CMaaString _sFormat(int SrcLine, const char* SrcFile, const char* format, ...) noexcept;
    static CMaaString _sFormat(int SrcLine, const char* SrcFile, CMaaString format, ...) noexcept;

    static CMaaString _sFormat2(int SrcLine, const char* SrcFile, const char* format, const char* text, ...) noexcept;
    static CMaaString _sFormat2(int SrcLine, const char* SrcFile, const char* format, CMaaString text, ...) noexcept;
    static CMaaString _sFormat2(int SrcLine, const char* SrcFile, CMaaString format, const char* text, ...) noexcept;
    static CMaaString _sFormat2(int SrcLine, const char* SrcFile, CMaaString format, CMaaString text, ...) noexcept;

    CMaaString& _Format(int SrcLine, const char* SrcFile, const char* format, ...) noexcept;
    CMaaString& _Format(int SrcLine, const char* SrcFile, CMaaString format, ...) noexcept;

    CMaaString& _Format2(int SrcLine, const char* SrcFile, const char* format, const char* text, ...) noexcept;
    CMaaString& _Format2(int SrcLine, const char* SrcFile, const char* format, CMaaString text, ...) noexcept;
    CMaaString& _Format2(int SrcLine, const char* SrcFile, CMaaString format, const char* text, ...) noexcept;
    CMaaString& _Format2(int SrcLine, const char* SrcFile, CMaaString format, CMaaString text, ...) noexcept;

    CMaaString& _FormatV(int SrcLine, const char* SrcFile, const char* format, va_list list) noexcept;
    CMaaString& _FormatV(int SrcLine, const char* SrcFile, CMaaString format, va_list list) noexcept;

    CMaaString& _FormatV2(int SrcLine, const char* SrcFile, const char* format, const char* text, va_list list) noexcept;
    CMaaString& _FormatV2(int SrcLine, const char* SrcFile, const char* format, CMaaString text, va_list list) noexcept;
    CMaaString& _FormatV2(int SrcLine, const char* SrcFile, CMaaString format, const char* text, va_list list) noexcept;
    CMaaString& _FormatV2(int SrcLine, const char* SrcFile, CMaaString format, CMaaString text, va_list list) noexcept;

    static CMaaString sFormat(const char* format, ...) noexcept;
    static CMaaString sFormat(CMaaString format, ...) noexcept;

    static CMaaString sFormat2(const char* format, const char* text, ...) noexcept;
    static CMaaString sFormat2(const char* format, CMaaString text, ...) noexcept;
    static CMaaString sFormat2(CMaaString format, const char* text, ...) noexcept;
    static CMaaString sFormat2(CMaaString format, CMaaString text, ...) noexcept;

    CMaaString& Format(const char* format, ...) noexcept;
    CMaaString& Format(CMaaString format, ...) noexcept;
    
    CMaaString& Format2(const char* format, const char* pText, ...) noexcept;
    CMaaString& Format2(const char* format, CMaaString text, ...) noexcept;
    CMaaString& Format2(CMaaString format, const char* text, ...) noexcept;
    CMaaString& Format2(CMaaString format, CMaaString text, ...) noexcept;

    CMaaString& FormatV(const char* format, va_list list) noexcept;
    CMaaString& FormatV(CMaaString format, va_list list) noexcept;

    CMaaString& FormatV2(const char* format, const char* text, va_list list) noexcept;
    CMaaString& FormatV2(const char* format, CMaaString text, va_list list) noexcept;
    CMaaString& FormatV2(CMaaString format, CMaaString text, va_list list) noexcept;
    CMaaString& FormatV2(CMaaString format, const char* text, va_list list) noexcept;

    CMaaString& Append(const char* format, ...) noexcept;
    CMaaString& Append(CMaaString format, ...) noexcept;

    CMaaString& Append2(const char* format, const char* text, ...) noexcept;
    CMaaString& Append2(const char* format, CMaaString text, ...) noexcept;
    CMaaString& Append2(CMaaString format, const char* text, ...) noexcept;
    CMaaString& Append2(CMaaString format, CMaaString text, ...) noexcept;

#ifndef __TOOLSLIB_REMOVE_OLD_CODE
    enum EStripType
    {
        eBegin    = 1,
        eEnd      = 2,
        eBoth     = 3,
        eCutCR    = 0x10,
        eCutLF    = 0x20,
        eCutCRLF  = 0x30,
        eCutEnd   = eCutLF, // Remove "\n" from end of string
        eCutSlash = 0x40    // Remove "\\" from end of string
    };
    CMaaString & Strip(EStripType Type = eBoth);
#endif

    CMaaString Left(int nCount) const;
    CMaaString Right(int nCount) const;
    CMaaString Mid(int nFirst, int nCount = -1) const;
    CMaaString RefLeft(int nCount) const noexcept;
    CMaaString RefRight(int nCount) const noexcept;
    CMaaString RefMid(int nFirst, int nCount = -1) const noexcept;
    bool IsLeft(const char * ptr, int nCount) const noexcept
    {
        return nCount >= 0 && Length() >= nCount && CMaaIsMemEq((const char *)*this, ptr, nCount);
    }
    bool IsRight(const char * ptr, int nCount) const noexcept
    {
        const int Len = Length();
        return Len >= nCount && CMaaIsMemEq(Len - nCount + (const char *)*this, ptr, nCount);
    }
    bool IsMid(const char * ptr, int nFirst, int nCount) const noexcept
    {
        const int Len = Length();
        return nCount >= 0 && nFirst >= 0 && Len >= nFirst + nCount && CMaaIsMemEq(nFirst + (const char *)*this, ptr, nCount);
    }
    bool IsCi(const char* ptr, int Len, int cp = -1) const noexcept //default -1: +rus // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    {
        return Length() == Len && ::IsCi((const char*)*this, ptr, Len, cp);
    }
    bool IsCi0(const char* ptr, int cp = -1) const noexcept //default -1: +rus // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    {
        return ::IsCi0((const char*)*this, ptr, Length(), cp);
    }
    bool IsLeftCi(const char* ptr, int nCount, int cp = -1) const noexcept //default -1: +rus // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    {
        return nCount >= 0 && Length() >= nCount && ::IsCi((const char*)*this, ptr, nCount, cp);
    }
    bool IsLeftCi0(const char* ptr, int cp = -1) const noexcept //default -1: +rus // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    {
        return ::IsCi0((const char*)*this, ptr, Length(), cp, true);
    }
    bool IsRightCi(const char* ptr, int nCount, int cp = -1) const noexcept //default -1: +rus // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    {
        const int Len = Length();
        return Len >= nCount && ::IsCi(Len - nCount + (const char*)*this, ptr, nCount, cp);
    }
    bool IsMidCi(const char* ptr, int nFirst, int nCount, int cp = -1) const noexcept //default -1: +rus // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    {
        const int Len = Length();
        return nCount >= 0 && nFirst >= 0 && Len >= nFirst + nCount && ::IsCi(nFirst + (const char*)*this, ptr, nCount, cp);
    }
    bool IsMidCi0(const char* ptr, int nFirst, int cp = -1) const noexcept //default -1: +rus // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    {
        const int Len = Length();
        return nFirst >= 0 && Len >= nFirst && ::IsCi0(nFirst + (const char*)*this, ptr, Len - nFirst, cp, true);
    }
    bool IsLeft(const CMaaString& That) const noexcept;
    bool IsRight(const CMaaString& That) const noexcept;
    bool IsMid(const CMaaString& That, int nFirst) const noexcept;
    bool IsCi(const CMaaString& That, int cp = -1) const noexcept;
    bool IsLeftCi(const CMaaString& That, int cp = -1) const noexcept;
    bool IsRightCi(const CMaaString& That, int cp = -1) const noexcept;
    bool IsMidCi(const CMaaString& That, int nFirst, int cp = -1) const noexcept;
    CMaaString Rewind() const;
    CMaaString RewindAdd(const CMaaString &AtBegin, const CMaaString &AtEnd); // rewind *this, add to result

    /*
    _dword Hash() const noexcept
    {
#ifdef CMAASTRING_CACHE_HASH
        if (m_pImp)
        {
            if ((m_pImp->m_Flags & CMaaStringImp::eHashExists))
            {
                return m_pImp->m_Hash;
            }
            _dword h = m_pImp->m_Hash = CMaaStaticStandardHashFunction((const unsigned char *)m_pImp->m_pszStr, m_pImp->m_iLength);
            m_pImp->m_Flags |= CMaaStringImp::eHashExists;
            return h;
        }
        return 0;
#else
        return CMaaStaticStandardHashFunction((const unsigned char*)(const char*)*this, Length());
#endif
    }
    */
    _uqword Hash64() const noexcept;
    int /*FoundStartPos or -1*/ FindIp(int StartPos, _IP * pIp = nullptr, int * pEndOfFoundIpPos = nullptr, int EndPos = -1, bool bStrict = false) const noexcept;
//#define TOOLSLIB_STAT_FIND
#define FINDN_RET_FIXED_1(x) -1 // (x) // -1 // (x)
    static bool KMPComputePrefixFunction(const char * pP, int m, CMaaPtr<int>& f) noexcept;
    bool KMPComputePrefixFunction(CMaaPtr<int>& f) const noexcept;
    // Knuth-Morris-Pratt fast substring find algorithm, uses int[P.Length()] temp prefix array, return first shift or -1 not found or -2 alloc error or throw
    int KMPFind(int StartPos, const void* SubStr, int SubStrLen = -1, int EndPos = -1) const noexcept;
    // Originally KMPFind2 is not the same as Find2: was
    // abcdefghij 1, "cde", "bcdef" : 1 1 // Find2
    // abcdefghij 1, "cde", "bcdef" : 2 0 // KMPFind2
    // Find SubStr1 or SubStr2 what is earlier
    int KMPFind2(int StartPos, const char* SubStr1, const char* SubStr2, int SubStr1Len = -1, int SubStr2Len = -1, int* pIndex = nullptr, int EndPos = -1) const noexcept; // Typically EndPos is Length() - the position after the end of source string
    // Find SubStr1 or SubStr2 or SubStr3 what is earlier
    int KMPFind3(int StartPos, const char* SubStr1, const char* SubStr2, const char* SubStr3, int SubStr1Len = -1, int SubStr2Len = -1, int SubStr3Len = -1, int* pIndex = nullptr, int EndPos = -1) const noexcept; // Typically EndPos is Length() - the position after the end of source string
    int KMPFind(const char* SubStr, int SubStrLen = -1) const noexcept;
    int KMPFind(const CMaaString &P) const noexcept;
    // Knuth-Morris-Pratt fast all substring's find algorithm, uses int[P.Length()] temp prefix array, return Ret: pos[Ret] array of shifts or Ret=-1 not found or Ret=-2 alloc error or throw
    int KMPFindAll(const CMaaString &P, CMaaPtr<int> &pos) const;
    template<typename Fn> int KMPFindAll(int StartPos, const void* SubStr, int SubStrLen, int EndPos, Fn Proc) const noexcept(noexcept(Proc(Length())))
    {
        if (StartPos < 0)
        {
            return -1;
        }
        if (SubStrLen == -1)
        {
            SubStrLen = int_strlen((const char*)SubStr);
        }
#ifdef TOOLSLIB_STAT_FIND00
        {
            int xx = 0;
            ghToolsLbStatFind.Find(CMaaString((const char*)SubStr, SubStrLen), &xx);
            xx++;
            ghToolsLbStatFind.AddOver(CMaaString((const char*)SubStr, SubStrLen), xx);
        }
#endif
        const int Len = EndPos != -1 && EndPos <= Length() ? EndPos : Length();
        if (StartPos + SubStrLen > Len || SubStrLen <= 0)
        {
            return -1;
        }

        const int n = Len - StartPos;
        const int m = SubStrLen;
        CMaaPtr<int> f(m, -1);
        const char* pT = StartPos + (const char*)*this;
        const char* pP = (const char*)SubStr;
        if (!KMPComputePrefixFunction(pP, m, f))
        {
            return FINDN_RET_FIXED_1(-2); // -1?  !! -2 === new[] err
        }
        int Ret = 0;
        int* pf = f;
        int q = 0;
        for (int i = 0; i < n; i++)
        {
            while (q > 0 && pP[q] != pT[i])
            {
                q = pf[q - 1];
            }
            if (pP[q] == pT[i])
            {
                q++;
            }
            if (q == m)
            {
                //Proc(*this, P, i + 1 - m);
                if (Proc(i + 1 - m))
                {
                    Ret++;
                }
                q = pf[q - 1];
            }
        }
        return Ret;
    }
    template<typename Fn> int FindAll(int StartPos, const void* SubStr, int SubStrLen, int EndPos, Fn Proc) const noexcept(noexcept(Proc(Length())))
    {
        if (StartPos < 0)
        {
            return -1;
        }
        if (SubStrLen == -1)
        {
            SubStrLen = int_strlen((const char*)SubStr);
        }
#ifdef TOOLSLIB_STAT_FIND00
        {
            int xx = 0;
            ghToolsLbStatFind.Find(CMaaString((const char*)SubStr, SubStrLen), &xx);
            xx++;
            ghToolsLbStatFind.AddOver(CMaaString((const char*)SubStr, SubStrLen), xx);
        }
#endif
        const int Len = EndPos != -1 && EndPos <= Length() ? EndPos : Length();
        if (StartPos + SubStrLen > Len || SubStrLen <= 0)
        {
            return 0;
        }
        const char* w = (const char*)SubStr;
        const char* s = StartPos + (const char*)*this;
        const char ch0 = w[0];
        int Hw = 0, Hs = 0;
        for (int i = 0; i < SubStrLen; i++)
        {
            Hw += (unsigned char)w[i];
            Hs += (unsigned char)s[i];
        }
        const char ch1 = w[SubStrLen - 1];
        int Ret = 0;
        for (int pos = StartPos; ;)
        {
            if (Hw == Hs && *s == ch0 && s[SubStrLen - 1] == ch1 && CMaaIsMemEq(s, SubStr, SubStrLen - 1))
            {
                if (Proc(pos))
                {
                    Ret++;
                }
            }
            if (++pos > Len - SubStrLen)
            {
                break;
            }
            Hs += (unsigned char)s[SubStrLen] - (unsigned char)*s;
            s++;
        }
        return Ret;
    }
#ifndef USE_FIND_KMP
    int Find(int StartPos, const void * SubStr, int SubStrLen = -1, int EndPos = -1) const noexcept; // Typically EndPos is Length() - the position after the end of source string
#else
    int Find(int StartPos, const void* SubStr, int SubStrLen = -1, int EndPos = -1) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        const int r = KMPFind(StartPos, SubStr, SubStrLen, EndPos);
#if FINDN_RET_FIXED_1(-2) == -2
        return r >= 0 ? r : -1;
#else
        return r;
#endif
    }
#endif
    // pRetN: -1 - not found, -2 - search exited by the other string found
    // Find SubStr1 or SubStr2 what is earlier
#ifndef USE_FIND_KMP
    int Find2(int StartPos, const char* SubStr1, const char* SubStr2, int SubStr1Len = -1, int SubStr2Len = -1, int* pIndex = nullptr, int EndPos = -1) const noexcept; // Typically EndPos is Length() - the position after the end of source string
#else
    int Find2(int StartPos, const char* SubStr1, const char* SubStr2, int SubStr1Len = -1, int SubStr2Len = -1, int* pIndex = nullptr, int EndPos = -1) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        const int r = KMPFind2(StartPos, SubStr1, SubStr2, SubStr1Len, SubStr2Len, pIndex, EndPos);
#if FINDN_RET_FIXED_1(-2) == -2
        return r >= 0 ? r : -1;
#else
        return r;
#endif
    }
#endif
    // Find SubStr1 or SubStr2 or SubStr3 what is earlier
#ifndef USE_FIND_KMP
    int Find3(int StartPos, const char* SubStr1, const char* SubStr2, const char* SubStr3, int SubStr1Len = -1, int SubStr2Len = -1, int SubStr3Len = -1, int* pIndex = nullptr, int EndPos = -1) const noexcept; // Typically EndPos is Length() - the position after the end of source string
#else
    int Find3(int StartPos, const char* SubStr1, const char* SubStr2, const char* SubStr3, int SubStr1Len = -1, int SubStr2Len = -1, int SubStr3Len = -1, int* pIndex = nullptr, int EndPos = -1) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        const int r = KMPFind3(StartPos, SubStr1, SubStr2, SubStr3, SubStr1Len, SubStr2Len, SubStr3Len, pIndex, EndPos);
#if FINDN_RET_FIXED_1(-2) == -2
        return r >= 0 ? r : -1;
#else
        return r;
#endif
    }
#endif
    enum
    {
        eFindN_Unknown = 0x0,
        eFindN_StrOk = 0x1, // all str lens > 0, str[0] is the smallest length string
        eFindN_ReturnTailPos = 0x2 // return pos + strlen
    };
protected:
    template<int N> int FindN__(int StartPos, const char** SubStr, const int* SubStrLen, int EndPos, int Flags, const int *Index, int *pIndex) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        const int Len = EndPos != -1 && EndPos <= Length() ? EndPos : Length();
        const int SubStr1Len = SubStrLen[0];
        char ch0[N], ch1[N];
        int Hw[N], Hs[N];
        int i, j;
        for (i = 0; i < N; i++)
        {
            ch0[i] = SubStr[i][0];
            ch1[i] = SubStr[i][SubStrLen[i] - 1];
            Hw[i] = 0;
            Hs[i] = 0;
        }
        const char* s = StartPos + (const char*)*this;
        for (j = 0; j < N; j++)
        {
            for (i = 0; i < SubStrLen[j]; i++)
            {
                Hw[j] += (unsigned char)SubStr[j][i];
                Hs[j] += (unsigned char)s[i];
            }
        }

        for (int pos = StartPos; ;)
        {
            if (Hw[0] == Hs[0] && *s == ch0[0] && s[SubStr1Len - 1] == ch1[0] && CMaaIsMemEq(s, SubStr[0], SubStr1Len - 1))
            {
                if (pIndex)
                {
                    *pIndex = Index[0];
                }
                return pos + ((Flags & eFindN_ReturnTailPos) ? SubStr1Len : 0);
            }
            for (j = 1; j < N; j++)
            {
                if (pos + SubStrLen[j] <= Len && Hw[j] == Hs[j] && *s == ch0[j] && s[SubStrLen[j] - 1] == ch1[j] && CMaaIsMemEq(s, SubStr[j], SubStrLen[j] - 1))
                {
                    if (pIndex)
                    {
                        *pIndex = Index[j];
                    }
                    return pos + ((Flags & eFindN_ReturnTailPos) ? SubStrLen[j] : 0);
                }
            }
            if (++pos > Len - SubStr1Len)
            {
                break;
            }
            Hs[0] += (unsigned char)s[SubStr1Len] - (unsigned char)*s;
            for (j = 1; j < N; j++)
            {
                if (pos + SubStrLen[j] <= Len)
                {
                    Hs[j] += (unsigned char)s[SubStrLen[j]] - (unsigned char)*s;
                }
            }
            s++;
        }
        /*
        if (pIndex)
        {
            *pIndex = -1;
        }
        */
        return -1;
    }
    template<int N> int KMPFindN__(int StartPos, const char** SubStr, const int* SubStrLen, int EndPos, const int** pf, int Flags, const int *Index, int *pIndex) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        const int Len = EndPos != -1 && EndPos <= Length() ? EndPos : Length();
        //const int SubStr1Len = SubStrLen[0];
        //int MaxLen = SubStr2Len > SubStr1Len ? SubStr2Len : SubStr1Len; ...
        int j;
        int MaxLen = SubStrLen[0];
        int n = Len - StartPos;
        //const int m = SubStrLen;
        //CMaaPtr<int> f[N];
        const char* pT = StartPos + (const char*)*this;
        const char** pP = SubStr;//[N];
        //int* pf[N];
        int q[N] = {};
        for (j = 0; j < N; j++)
        {
            if (SubStrLen[j] > MaxLen)
            {
                MaxLen = SubStrLen[j];
            }
            /*
            CMaaPtr<int> ff(SubStrLen[j], -1);
            f[j].Swap(ff);
            if (!KMPComputePrefixFunction(pP[j], SubStrLen[j], f[j]))
            {
                return FINDN_RET_FIXED_1(-2); // -1?  !! -2 === new[] err
            }
            pf[j] = f[j];
            */
        }
        int Pos = -1, Ret = -1, idx = -1;
        for (int i = 0; i < n; i++)
        {
            for (j = 0; j < N; j++)
            {
                while (q[j] > 0 && pP[j][q[j]] != pT[i])
                {
                    q[j] = pf[j][q[j] - 1];
                }
                if (pP[j][q[j]] == pT[i])
                {
                    q[j]++;
                }
                if (q[j] == SubStrLen[j])
                {
                    if (Pos < 0 || StartPos + (i + 1 - SubStrLen[j]) < Pos || (StartPos + (i + 1 - SubStrLen[j]) == Pos && Index[j] < idx))
                    {
                        if (Pos < 0 && i + MaxLen - SubStrLen[j] + 1 < n)
                        {
                            n = i + MaxLen - SubStrLen[j] + 1;
                        }
                        Pos = StartPos + (i + 1 - SubStrLen[j]);
                        Ret = Pos + ((Flags & eFindN_ReturnTailPos) ? SubStrLen[j] : 0);
                        idx = Index[j];
                    }
                    q[j] = pf[j][q[j] - 1];
                }
            }
        }
        if (/*Pos >= 0 &&*/ pIndex)
        {
            *pIndex = idx;
        }
        return Ret;
    }
    template<int N> int FindN_(int StartPos, int n, const char** SubStr, const int* SubStrLen, int EndPos, int Flags, const int *Index, int *pIndex) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        return n < N ? 
            FindN_<N - 1>(StartPos, n, SubStr, SubStrLen, EndPos, Flags, Index, pIndex) :
            FindN__<N>(StartPos, SubStr, SubStrLen, EndPos, Flags, Index, pIndex);
    }
    template<int N> int KMPFindN_(int StartPos, int n, const char** SubStr, const int* SubStrLen, int EndPos, const int** pf, int Flags, const int *Index, int *pIndex) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        return n < N ? 
            KMPFindN_<N - 1>(StartPos, n, SubStr, SubStrLen, EndPos, pf, Flags, Index, pIndex) :
            KMPFindN__<N>(StartPos, SubStr, SubStrLen, EndPos, pf, Flags, Index, pIndex);
    }
public:
#ifndef USE_FIND_KMP
    template<int N> struct sFindNCalcData
    {
        int SubStrLen[N];
        sFindNCalcData() noexcept
        {
            //memset(SubStrLen, -1, sizeof(SubStrLen));
            SubStrLen[0] = -1;
        }
        sFindNCalcData(const char ** Str) noexcept
        {
            for (int i = 0; i < N; i++)
            {
                SubStrLen[i] = int_strlen(Str[i]);
            }
        }
    };
    template<int N> int FindN(int StartPos, const char** SubStr, int* SubStrLen = nullptr, int* pIndex = nullptr, int EndPos = -1, int Flags = eFindN_StrOk) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        int _SubStrLen[N], Index[N];
        pcCHAR_CMaaTL _SubStr[N];
        //int tmpIndex;
        if (!SubStrLen)
        {
            //memset(SubStrLen = _SubStrLen, -1, N * sizeof(int));
            SubStrLen = _SubStrLen;
            SubStrLen[0] = -1;
        }
        if (pIndex)
        {
            *pIndex = -1;
        }
        if (StartPos < 0)
        {
            return -1;
        }
        memcpy(_SubStr, SubStr, N * sizeof(const char*));
        //memcpy(_SubStrLen, SubStrLen, N * sizeof(int));
        int i;
        if (SubStrLen[0] == -1)
        {
            for (i = 0; i < N; i++)
            {
                Index[i] = i;
                //_SubStr[i] = SubStr[i];
                //_SubStrLen[i] = SubStrLen[i];
                SubStrLen[i] = int_strlen(SubStr[i]);
            }
        }
        else
        {
            for (i = 0; i < N; i++)
            {
                Index[i] = i;
            }
        }
        SubStr = _SubStr;
        //SubStrLen = _SubStrLen;
        const int Len = EndPos != -1 && EndPos <= Length() ? EndPos : Length();
        int j = 0;
        for (i = 0; i < N; i++)
        {
            if (SubStrLen[i] > 0 && StartPos + SubStrLen[i] <= Len)
            {
                SubStr[j] = SubStr[i];
                _SubStrLen[j] = SubStrLen[i];
                Index[j] = Index[i];
                if (!(Flags & eFindN_StrOk) && _SubStrLen[j] < _SubStrLen[0])
                {
                    CMaaSwap(SubStr[j], SubStr[0]);
                    CMaaSwap(_SubStrLen[j], _SubStrLen[0]);
                    CMaaSwap(Index[j], Index[0]);
                }
                j++;
            }
        }
        return FindN_<N>(StartPos, j, SubStr, _SubStrLen, EndPos, Flags, Index, pIndex);// | eFindN_StrOk);
    }
#else
    template<int N> int FindN(int StartPos, const char** SubStr, int* SubStrLen = nullptr, int* pIndex = nullptr, int EndPos = -1, int Flags = eFindN_StrOk) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        const int r = KMPFindN<N>(StartPos, SubStr, SubStrLen, pIndex, EndPos, nullptr, Flags);
#if FINDN_RET_FIXED_1(-2) == -2
        return r >= 0 ? r : -1;
#else
        return r;
#endif
    }
#endif
    template<int N> struct sKMPFindNCalcData
    {
        int SubStrLen[N];
        CMaaPtr<int> f[N];
        sKMPFindNCalcData(const sKMPFindNCalcData &That) = delete;
        sKMPFindNCalcData & operator=(const sKMPFindNCalcData &That) = delete;
        sKMPFindNCalcData(bool bInit = true) noexcept
        {
            if (bInit)
            {
                memset(SubStrLen, -1, sizeof(SubStrLen));
            }
        }
    };
    template<int N> int KMPFindN(int StartPos, const char** SubStr, int* SubStrLen = nullptr, int* pIndex = nullptr, int EndPos = -1, sKMPFindNCalcData<N> * f = nullptr, int Flags = eFindN_StrOk) const noexcept // Typically EndPos is Length() - the position after the end of source string
    {
        int _SubStrLen[N], Index[N];
        pcCHAR_CMaaTL _SubStr[N];
        const int* pf[N];
        sKMPFindNCalcData<N> ff(!f);
        f = f ? f : &ff;
        //int tmpIndex;
        if (!SubStrLen)
        {
            SubStrLen = f->SubStrLen;
            //memset(SubStrLen = _SubStrLen, -1, N * sizeof(int));
            //for (i = 0; i < N; i++)
            //{
            //    SubStrLen[i] = -1;
            //}
        }
        if (pIndex)
        {
            *pIndex = -1;
        }
        if (StartPos < 0)
        {
            return -1;
        }
        memcpy(_SubStr, SubStr, N * sizeof(const char*));
        //memcpy(_SubStrLen, SubStrLen, N * sizeof(int));
        int i;
        for (i = 0; i < N; i++)
        {
            Index[i] = i;
            //_SubStr[i] = SubStr[i];
            //_SubStrLen[i] = SubStrLen[i];
            if (SubStrLen[i] == -1)
            {
                SubStrLen[i] = int_strlen(SubStr[i]);
            }
        }
        SubStr = _SubStr;
        const int Len = EndPos != -1 && EndPos <= Length() ? EndPos : Length();
        int j = 0;
        for (i = 0; i < N; i++)
        {
            if (SubStrLen[i] > 0 && StartPos + SubStrLen[i] <= Len)
            {
                SubStr[j] = SubStr[i];
                _SubStrLen[j] = SubStrLen[i];
                Index[j] = Index[i];
                if ((int)f->f[i].MaxIndex() != SubStrLen[i])
                {
                    CMaaPtr<int> ff(SubStrLen[i], -1);
                    f->f[i].Swap(ff);
                    if (!KMPComputePrefixFunction(SubStr[i], SubStrLen[i], f->f[i]))
                    {
                        return -2;//FINDN_RET_FIXED_1(-2); // -1?  !! -2 === new[] err
                    }
                }
                pf[j] = f->f[i];
                /*
                if (!(Flags & eFindN_StrOk) && SubStrLen[j] < SubStrLen[0])
                {
                    CMaaSwap(SubStr[j], SubStr[0]);
                    CMaaSwap(SubStrLen[j], SubStrLen[0]);
                    CMaaSwap(Index[j], Index[0]);
                }
                */
                j++;
            }
        }
        return KMPFindN_<N>(StartPos, j, SubStr, _SubStrLen, EndPos, pf, Flags, Index, pIndex);// | eFindN_StrOk);
    }
    int FindN_old(int StartPos, int N, const char** SubStr, int* SubStrLen = nullptr, int* pPos = nullptr, int EndPos = -1, int Flags = eFindN_StrOk) const noexcept; // N <= 10 Typically EndPos is Length() - the position after the end of source string // FindN_old(,3,...) is 2.5% lower then FindN<3>(...)
    int FindNEx(int StartPos, int N, const char **SubStr, int *SubStrLen = nullptr, int *pPos = nullptr, int EndPos = -1, int Flags = eFindN_Unknown) const; // Typically EndPos is Length() - the position after the end of source string
    int Find(int StartPos, char c, int EndPos = -1) const noexcept;
    // Find c1 or c2
    int Find2(int StartPos, char c1, char c2, int EndPos = -1) const noexcept;
    // Find c1 or c2 or c3
    int Find3(int StartPos, char c1, char c2, char c3, int EndPos = -1) const noexcept;
    int Find(int StartPos, const CMaa256Bits &Bits, int EndPos = -1) const noexcept;
    int FindCrLf(int StartPos, int * pCrLfLen = nullptr, int * pOffset_from_StartPos_i_e_LineLength = nullptr, int EndPos = -1, int * pAutoMovePos = nullptr, CMaaString * pCrLf = nullptr) const noexcept;
#ifndef USE_FIND_KMP
    int Find(const char* substr) const noexcept;
#else
    int Find(const char* substr) const noexcept
    {
        const int r = KMPFind(0, substr);
#if FINDN_RET_FIXED_1(-2) == -2
        return r >= 0 ? r : -1;
#else
        return r;
#endif
    }
#endif
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    int Find(const char8_t* substr) const noexcept;
#endif
    int Find(char c) const noexcept;
    int Find(const CMaa256Bits& Bits) const noexcept;
    int Find() const noexcept = delete;
#ifndef USE_FIND_KMP
    int Find(const CMaaString& That) const noexcept;
    int Find(int StartPos, const CMaaString& That, int EndPos = -1) const noexcept;
#else
    int Find(const CMaaString& That) const noexcept
    {
        const int r = KMPFind(That);
#if FINDN_RET_FIXED_1(-2) == -2
        return r >= 0 ? r : -1;
#else
        return r;
#endif
    }
    int Find(int StartPos, const CMaaString& That, int EndPos = -1) const noexcept
    {
        const int r = KMPFind(StartPos, (const char*)That, That.Length(), EndPos);
#if FINDN_RET_FIXED_1(-2) == -2
        return r >= 0 ? r : -1;
#else
        return r;
#endif
    }
#endif
    int ReverseFind(int StartPos, const void * SubStr, int SubStrLen = -1, int EndPos = -1) const noexcept; // Typically EndPos is a -1 - the position before start of source string
    int ReverseFind(int StartPos, char c, int EndPos = -1) const noexcept;
    int ReverseFind(int StartPos, const CMaa256Bits &Bits, int EndPos = -1) const noexcept;
    int ReverseFind(const char * substr) const noexcept;
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    int ReverseFind(const char8_t * substr) const noexcept;
#endif
    int ReverseFind(char c) const noexcept;
    int ReverseFind(const CMaa256Bits &Bits) const noexcept;
    int ReverseFind() const noexcept = delete;
    int ReverseFind(const CMaaString &That) const noexcept;
    int ReverseFind(int StartPos, const CMaaString &That, int EndPos = -1) const noexcept;
    int Fill(int StartPos, int Len, char c);
    int Fill(char c = 0);
    int Copy(int pos, const void * ptr, int size);
    int Copy(int pos, const CMaaString &src, int src_offset, int length);

    // Java-style System.arraycopy(src, src_offset, dst, dst_offset, length);
    // --> dst.Copy(src, src_offset, dst_offset, length);
    int Copy(const CMaaString &src, int src_offset, int dst_offset, int length);

    CMaaString UnicodeLittleUnmarked();

    int GetLength() const noexcept
    {
        return Length();
    }

    // import binary string (can not to have terminating '\0')
    bool Import(const void * ptr, int maxlen); // returns false on alloc error
    // export binary string (can not to add terminating '\0' to ptr, if string length >= maxlen)
    bool Export(void * ptr, int maxlen) const noexcept; // returns false if string was truncated
    /*
    void Empty() noexcept
    {
        //*this = CMaaString{};
        CMaaStringImp* p = m_pImp;
        m_pImp = nullptr;
        if (p)
        {
#ifdef NEW_DELETE_PRINTF
            //printf("str: release %p\n", p); // !NEW
#endif
            if (!p->Release())
            {
#if MANUAL_STRINGS_REFS
                delete p;
#endif
            }
        }
    }
    */
    // returns number of replacements, -1 on new[] error
    int ReplaceNN(char What, char By, int StartPos = 0, int EndPos = -1);
    int ReplaceNN(char What, const CMaaString &By, int StartPos = 0, int EndPos = -1);
    int ReplaceNN(char What, const char * strBy, int StartPos = 0, int EndPos = -1)
    {
        CMaaString By(strBy, eStrlenMemString);
        return ReplaceNN(What, By, StartPos, EndPos);
    }
    int ReplaceNN(int N, const char * pWhat, const CMaaString * const pBy, int StartPos = 0, int EndPos = -1);
#ifndef USE_FIND_KMP
    // returns number of replacements, -1 on new[] error
    int ReplaceNN(const CMaaString &What, const CMaaString &By, bool bCountOnly = false, int StartPos = 0, int EndPos = -1);
#else
    int ReplaceNN(const CMaaString &What, const CMaaString &By, bool bCountOnly = false)
    {
        return KMPReplaceNN(What, By, bCountOnly);
    }
#endif
    int ReplaceNN(const char* strWhat, const char* strBy, bool bCountOnly = false)
    {
        CMaaString What(strWhat, eStrlenMemString), By(strBy, eStrlenMemString);
        return ReplaceNN(What, By, bCountOnly);
    }
    int ReplaceNN(const CMaaString &What, const char* strBy, bool bCountOnly = false)
    {
        CMaaString By(strBy, eStrlenMemString);
        return ReplaceNN(What, By, bCountOnly);
    }
    int ReplaceNN(const char* strWhat, const CMaaString &By, bool bCountOnly = false)
    {
        CMaaString What(strWhat, eStrlenMemString);
        return ReplaceNN(What, By, bCountOnly);
    }

    // *pNN - number of replacements, returns CMaaStringZ and *pNN = -1 on new[] error
    CMaaString Replace(char What, char By, bool bStoreResult = true, int *pNN = nullptr);
    // *pNN - number of replacements, returns CMaaStringZ and *pNN = -1 on new[] error
    CMaaString Replace(const CMaaString &What, const CMaaString &By, bool bStoreResult = true, int* pNN = nullptr);
    CMaaString Replace(const char* strWhat, const char* strBy, bool bStoreResult = true, int* pNN = nullptr)
    {
        CMaaString What(strWhat, eStrlenMemString), By(strBy, eStrlenMemString);
        return Replace(What, By, bStoreResult, pNN);
    }
    CMaaString Replace(const CMaaString &What, const char* strBy, bool bStoreResult = true, int* pNN = nullptr)
    {
        CMaaString By(strBy, eStrlenMemString);
        return Replace(What, By, bStoreResult, pNN);
    }
    CMaaString Replace(const char* strWhat, const CMaaString &By, bool bStoreResult = true, int* pNN = nullptr)
    {
        CMaaString What(strWhat, eStrlenMemString);
        return Replace(What, By, bStoreResult, pNN);
    }

#ifdef TOOLSLIB_CHAR8T_SUPPORT
    int ReplaceNN(const char8_t* strWhat, const char8_t* strBy, bool bCountOnly = false)
    {
        CMaaString What(strWhat, eStrlenMemString), By(strBy, eStrlenMemString);
        return ReplaceNN(What, By, bCountOnly);
    }
    int ReplaceNN(const CMaaString &What, const char8_t* strBy, bool bCountOnly = false)
    {
        CMaaString By(strBy, eStrlenMemString);
        return ReplaceNN(What, By, bCountOnly);
    }
    int ReplaceNN(const char8_t* strWhat, const CMaaString &By, bool bCountOnly = false)
    {
        CMaaString What(strWhat, eStrlenMemString);
        return ReplaceNN(What, By, bCountOnly);
    }
#endif
    int KMPReplaceNN(const CMaaString &What, const CMaaString &By, bool bCountOnly = false);
    bool IsSingleOwner() const noexcept
    {
        return !m_pImp || m_pImp->IsSingleOwner();
    }
    bool IsRWSingleOwner() const noexcept
    {
        return !m_pImp || m_pImp->IsRWSingleOwner();
    }
    bool HasReffered() const noexcept
    {
        return m_pImp && m_pImp->HasReffered();
    }
    bool Is0Behind() const noexcept
    {
        return !m_pImp || m_pImp->Is0Behind();
    }
    bool IsWC0Behind() const noexcept
    {
        return !m_pImp || m_pImp->IsWC0Behind();
    }
    bool IsWChar0Behind() const noexcept
    {
        return !m_pImp || m_pImp->IsWChar0Behind();
    }
    bool IsWC32_0Behind() const noexcept
    {
        return !m_pImp || m_pImp->IsWC32_0Behind();
    }
    // It is not need to known if the string is 'Memory' string in general way
    bool IsMemoryString() const noexcept
    {
        return m_pImp && m_pImp->IsMemoryString();
    }
    bool IsROString() const noexcept
    {
        return m_pImp && m_pImp->IsROString();
    }
    bool IsMemoryNotROString() const noexcept // is memory, but not const string
    {
        return m_pImp && m_pImp->IsMemoryNotROString();
    }
    bool IsMatchMask(const CMaaString &Mask) const; // Case sensitive mask match
    //#ifdef _UNICODE
#if defined(_UNICODE) || defined(__unix__)
    bool IsMatchMask__(const CMaaString &Mask) const {CMaaString u = ::Utf8ToUnicode(*this); return u.IsMatchUnicodeMask(::Utf8ToUnicode(Mask));}
#else
    bool IsMatchMask__(const CMaaString &Mask) {return IsMatchMask(Mask);}
#endif
    bool IsMatchUnicodeMask(const CMaaString &Mask) const;
    bool IsMatchFileMask(const CMaaString &Mask, int bCaseSensitive = 0/*-1 - no, 1 - yes, 0 - default*/) const;
    CMaaString ToLower(int cp = -1 /*default: +rus*/) const; // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    CMaaString ToUpper(int cp = -1 /*default: +rus*/) const; // 0: acts for englist letters only, 1251 - +rus, e_utf8 (rus+lat), e_utf8_rus (rus+lat)
    int CountChars(char c, int Start = 0, int End = -1) const noexcept;
    CMaaString GetArg();
    CMaaString CharToOem() const;
    CMaaString OemToChar() const;
    int RussianAscii1251CharsCount(int CountOne = 0) const noexcept;
    int RussianOemCharsCount(int CountOne = 0) const noexcept;
    int Utf8SpecificCharsCount(int CountOne = 0) const noexcept;
    int Utf8PartOfCharsLength() const noexcept;
    CMaaString Translit(int cp = 1251) const;
    CMaaString FromTranslit(int *translated_chars_count = nullptr, int *unchanged_chars_count = nullptr, int allowed_untranslated_chars_count = -1) const;
    CMaaString Base64Encode(int LineLen = -1, CMaaString Lf = CMaaStringCrLf) const;
    CMaaString Base64Decode(bool *pbOk = nullptr, int *pValidCharNums = nullptr, CMaaString IgnoreChars = CMaaStringCrLf) const;
    CMaaString MyEncode(int LineLen = -1, CMaaString Lf = CMaaStringCrLf) const; // web encode generic alphabet based
    CMaaString MyDecode(bool *pbOk = nullptr, int *pValidCharNums = nullptr, CMaaString IgnoreChars = CMaaStringCrLf) const; // web decode generic alphabet based
    CMaaString ToMD5() const;
    CMaaString DisplayHex(bool bUpper = false) const;
    CMaaString FromDisplayedHex(bool *pbErr0 = nullptr) const;
    CMaaString RemoveSpaces(bool bAtBegin = true, bool bAtEnd = true, CMaaString Spaces = s_szSpaces) const;
    CMaaString RemoveCrLfAtEOL() const;
    CMaaString GetWord0(const CMaa256Bits &Bits = gSpacesBits); // unsafe mod *this string
    CMaaString GetWord0(char c); // unsafe mod *this string
    CMaaString GetWord0(CMaaString Spaces) // unsafe mod *this string
    {
        return GetWord0(CMaa256Bits(Spaces, Spaces.Length()));
    }
    CMaaString GetWord0(const char * pszSpaces) // unsafe mod *this string
    {
        return GetWord0(CMaa256Bits(pszSpaces, int_strlen(pszSpaces)));
    }
    CMaaString GetWord(const CMaa256Bits &Bits = gSpacesBits, bool bRemoveWord = true, bool bRemoveSpacesAtTheBeginning = true, bool bOptMid = false);
    CMaaString GetWord(char c, bool bRemoveWord = true, bool bRemoveSpacesAtTheBeginning = true, bool bOptMid = false);
    CMaaString GetWord(bool bRemoveWord, bool bRemoveSpacesAtTheBeginning, CMaaString Spaces, bool bOptMid = false) = delete;
    CMaaString GetWord(bool bRemoveWord, bool bRemoveSpacesAtTheBeginning = true, const char * pszSpaces = " \t", bool bOptMid = false) = delete;
    CMaaString GetWord(CMaaString Spaces /*= " \t"*/, bool bRemoveWord = true, bool bRemoveSpacesAtTheBeginning = true, bool bOptMid = false)
    {
        return GetWord(CMaa256Bits(Spaces, Spaces.Length()), bRemoveWord, bRemoveSpacesAtTheBeginning, bOptMid);
    }
    CMaaString GetWord(const char * pszSpaces /*= " \t"*/, bool bRemoveWord = true, bool bRemoveSpacesAtTheBeginning = true, bool bOptMid = false)
    {
        return GetWord(CMaa256Bits(pszSpaces, int_strlen(pszSpaces)), bRemoveWord, bRemoveSpacesAtTheBeginning, bOptMid);
    }
    /*
    CMaaString GetWord(CMaaString Spaces / * = " \t" * /)
    {
        return GetWord(Spaces, true, true);
    }
    CMaaString GetWord(const char * Spaces / * = " \t" * /)
    {
        return GetWord(Spaces, true, true);
    }
    */
    CMaaString GetCsvWord(CMaaString SrcCodePage = "1251");
    void AppendCsvWord(CMaaString ww, CMaaString DstCodePage = "1251");
    CMaaString GetLine0(/*bool bRemove = true, bool bRemoveCrLf = true, bool bOptimizeMid = true,*/ CMaaString* pCrLf = nullptr); // unsafe mod *this string
    CMaaString GetLine(bool bRemove = true, bool bRemoveCrLf = true, bool bOptimizeMid = true, CMaaString * pCrLf = nullptr);
    CMaaString GetLine(const CMaaString &crlf, bool bRemove = true, bool bRemoveCrLf = true, bool bOptimizeMid = true);
    CMaaString ConvertHttpParam() const; // for param name and value, result is utf8 string (usually)
    CMaaString ToHttpHtmlDisplayedText(bool bWeak = false, CMaaString strEsc = CMaaString{}) const;//CMaaString());
    //CMaaString ToHttpHtmlDisplayedText(bool bWeak, CMaaString strEsc);
    CMaaString Str2HtmlVal(CMaaString strEsc = "&\"'<>") const;
    CMaaString ToHttpHtmlAreaText() const;
    CMaaString ToHttpHtmlARef() const; // CMaaString::sFormat("<a href="%S">%S</a>", &ToHttpHtmlARef(Url), &ToHttpHtmlDisplayedText(Text));
    CMaaString ToHttpHtmlCgiParam() const; // ToHttpHtmlCgiParam() and encodeURIComponent() are the same
    CMaaString encodeURIComponent(bool bUppercaseHex = true) const; // ToHttpHtmlCgiParam() and encodeURIComponent() are the same
    CMaaString encodeURIComponents(bool bUppercaseHex = true) const; // components are '/' separated
    CMaaString decodeURIComponent() const; // decodeURIComponent() is UrlWithMixedUtf8ToUtf8(*this)
    CMaaString ToCanonicalUrl(CMaaString *pRs = nullptr, CMaaString *pHost = nullptr, CMaaString *pPath = nullptr, CMaaString *pArgs = nullptr, CMaaString *pIntHost = nullptr, bool bIntRet = false) const;
    bool SplitUrl(CMaaString &UrlPfx, CMaaString &Domain, CMaaString &UrlPath) const; // "http://" "суб.дом.рф" "/path" // return true always
    CMaaString IDN_EncodeDomainUrl() const;
    CMaaString IDN_DecodeDomainUrl() const;
    CMaaString ToJsArgValue(char quote = 0) const;
    CMaaString GetCookie(const char * pszCookieNameEq /*= "COOKIENAME3=" */, bool bOptMid = true) const
;
    CMaaString QuoteShArg(bool * pbErr = nullptr) const;
    bool IsValidVarName() const noexcept;
    bool IsValidPassword() const noexcept;
    bool IsValidPath(int Flags = 0) const noexcept; // Is simple path [0-9a-zA-Z_/.]
    CMaaString CutZeroFractionPart(bool bCutLeadingZerosToo = false) const;

    int sscanfInt(int *pRet) const noexcept; // sscanf(ptr,"%d", &x) - like
    int ToInt(int iDefault = 0, int iStrict = 0) const noexcept; // on overflow or errors return iDefault
    _qword ToQword(_qword qDefault = 0, int iStrict = 1) const noexcept; // on overflow or errors return qDefault
    
    template<typename T> T ToDouble_internal(T xDefault = 0, bool* pbOk = nullptr, int iStrict = 1) const noexcept; // on errors return xDefault
    float ToFloat(float xDefault = 0.0f, bool* pbOk = nullptr, int iStrict = 1) const noexcept; // on errors return xDefault
    double ToDouble(double xDefault = 0.0, bool* pbOk = nullptr, int iStrict = 1) const noexcept; // on errors return xDefault
    long double ToLongDouble(long double xDefault = 0.0, bool* pbOk = nullptr, int iStrict = 1) const noexcept; // on errors return xDefault

#if 0
#define ToString_noexcept
#else
#define ToString_noexcept noexcept
#define ToString_is_noexcept
#endif

    CMaaString& ToString(int x) ToString_noexcept;
    CMaaString& ToString(_qword x) ToString_noexcept;
    CMaaString& ToString(int x, char mode) ToString_noexcept; // mode = 0, '+', ',', '+'^','
    CMaaString& ToString(_qword x, char mode) ToString_noexcept;
    CMaaString& ToString(float x) noexcept;
    CMaaString& ToString(double x) noexcept;
    CMaaString& ToString(long double x) noexcept;

    static CMaaString sToString(int x) ToString_noexcept;
    static CMaaString sToString(_qword x) ToString_noexcept;
    static CMaaString sToString(int x, char mode) ToString_noexcept; // mode = 0, '+', ',', '+'^','
    static CMaaString sToString(_qword x, char mode) ToString_noexcept;
    static CMaaString sToString(float x) noexcept;
    static CMaaString sToString(double x) noexcept;
    static CMaaString sToString(long double x) noexcept;

#if 1
    int sscanfUInt(unsigned int *pRet) const noexcept; // sscanf(ptr,"%u", &x) - like
    unsigned int ToUInt(unsigned int iDefault = 0, int iStrict = 0) const noexcept; // on overflow or errors return iDefault
    _uqword ToUQword(_uqword qDefault = 0, int iStrict = 1) const noexcept; // on overflow or errors return qDefault

    CMaaString& ToString(unsigned int x) ToString_noexcept;
    CMaaString& ToString(_uqword x) ToString_noexcept;
    CMaaString& ToString(unsigned int x, char mode) ToString_noexcept;
    CMaaString& ToString(_uqword x, char mode) ToString_noexcept;
    static CMaaString sToString(unsigned int x) ToString_noexcept;
    static CMaaString sToString(_uqword x) ToString_noexcept;
    static CMaaString sToString(unsigned int x, char mode) ToString_noexcept;
    static CMaaString sToString(_uqword x, char mode) ToString_noexcept;
#else
    int sscanfUInt(unsigned int *pRet) const noexcept = delete; // sscanf(ptr,"%u", &x) - like
    unsigned int ToUInt(unsigned int iDefault = 0, int iStrict = 0) const noexcept = delete; // on overflow or errors return iDefault
    _uqword ToUQword(_uqword qDefault = 0, int iStrict = 1) const noexcept = delete; // on overflow or errors return qDefault

    CMaaString& ToString(unsigned int x) ToString_noexcept = delete;
    CMaaString& ToString(_uqword x) ToString_noexcept = delete;
    CMaaString& ToString(unsigned int x, char mode) ToString_noexcept = delete;
    CMaaString& ToString(_uqword x, char mode) ToString_noexcept = delete;
    static CMaaString sToString(unsigned int x) ToString_noexcept = delete;
    static CMaaString sToString(_uqword x) ToString_noexcept = delete;
    static CMaaString sToString(unsigned int x, char mode) ToString_noexcept = delete;
    static CMaaString sToString(_uqword x, char mode) ToString_noexcept = delete;
#endif
    CMaaString Extention(int WithDot = 1) const noexcept;
    CMaaString ReplaceAddExtention(const CMaaString &NewExtention);

    bool is_a_email(int * pErrorPos = nullptr, CMaaString * pResult = nullptr, int Flags = 0) const; // Flags: 1 for removing '<' *@* '>', 2 - DigMx, 4 - fail on dig error
    bool is_a_domain(int * pErrorPos = nullptr); // modidied string to lower
    CMaaString ToFsAllowed(bool brus = true, bool bdirectoryes = false, int * pErrNum = nullptr, int fCutPath = -1) const;
    CMaaString ToFsAndShellAllowed(bool brus = true, bool bdirectoryes = false, int * pErrNum = nullptr) const;

    bool ListHas(const CMaaString &prop, const CMaaString &d = CMaaStringSemicolon) const;
    bool ListNotHas(const CMaaString &prop, const CMaaString &d = CMaaStringSemicolon) const;
    bool ListAdd(const CMaaString &prop, int MaxLen = -1, const CMaaString &d = CMaaStringSemicolon);
    bool ListRem(const CMaaString &prop, const CMaaString &d = CMaaStringSemicolon);
    CMaaString List2Sql(const CMaaString &d = CMaaStringSemicolon) const;
    CMaaString List4Sql(const CMaaString &d = CMaaStringSemicolon) const;
    //CMaaString ListGetFirst(bool bRemove = true, CMaaString d = CMaaStringSemicolon);
    CMaaString Pass1Replace(CMaaUnivHash<CMaaString, CMaaString> &hVars, CMaaString Prefix = "{{{{{", CMaaString Suffix = "}}}}}") const;
    CMaaString Utf8ToUtf8_3_Compatible(char chReplace = '_', bool bCut = false) const noexcept;
    CMaaString Utf8ToUtf8_4_Compatible(char chReplace = '_', bool bCut = false) const noexcept;
    CMaaString ToCSourceString() const;

    struct XX
    {
        CMaaString &s;
        int idx;
        bool bConst;

        XX(CMaaString &x, int n) noexcept
        :   s(x),
            idx(n),
            bConst(false)
        {
        }
        XX(const CMaaString &x, int n) noexcept
        :   s((CMaaString &)x),
            idx(n),
            bConst(true)
        {
        }
        operator const char& () const noexcept
        {
            return (s.m_pImp && idx < s.m_pImp->m_iLength && idx >= 0) ? s.m_pImp->m_pszStr[idx] : sChar0_2[0]; // (sChar0_2[0] = 0);
        }
        const char * operator& () const noexcept
        {
            return &(const char &)*this;
        }
        const char& operator=(char c)
        {
            if  (s.m_pImp && s.Length() > idx && idx >= 0 && !bConst)
            {
                if  (!s.m_pImp->IsRWSingleOwner())
                {
                    CMaaString temp = s.NewCopy();
                    if  (temp.m_pImp)
                    {
                        s = temp;
                    }
                }
                if  (s.m_pImp && s.Length() > idx && s.m_pImp->IsRWSingleOwner())
                {
                    //s.ClearHash();
                    return *(idx + s.GetBuffer()) = c;
                }
            }
            static char c2;
            c2 = c;
            return c2;
        }
        const char& operator=(const XX& That)
        {
            return *this = (char)That;
        }
        /*
          bool operator == (const XX &That) const {return (char)*this == (char)That;}
          bool operator != (const XX &That) const {return (char)*this != (char)That;}
          bool operator > (const XX &That) const {return (char)*this > (char)That;}
          bool operator < (const XX &That) const {return (char)*this < (char)That;}
          bool operator >= (const XX &That) const {return (char)*this >= (char)That;}
          bool operator <= (const XX &That) const {return (char)*this <= (char)That;}
        */
    };

    XX operator[](int idx) noexcept
    {
        return XX(*this, idx);
    }

    XX operator[](size_t idx) noexcept
    {
        return XX(*this, (int)idx);
    }

    const char& operator[](int idx) const noexcept // rarely used
    {
        return (m_pImp && idx < m_pImp->m_iLength && idx >= 0) ? m_pImp->m_pszStr[idx] : sChar0_2[0]; // (sChar0_2[0] = 0);
    }

    const char& operator[](size_t idx) const noexcept // rarely used
    {
        return (m_pImp && idx < (size_t)m_pImp->m_iLength) ? m_pImp->m_pszStr[idx] : sChar0_2[0]; // (sChar0_2[0] = 0);
    }

#ifndef __TOOLSLIB_REMOVE_OLD_CODE
    static void StrCutSlash(char * Str);
    static void StrAddSlash(char * Str);
#endif

    const char * SetAdditionalData(const char * ptr, int length, int bDefaultNullAccept = 0);
    _WC_ * SetAdditionalData(const _WC_ * ptr, int length, int bDefaultNullAccept = 0);
    _WC_ * Utf8ToUnicode(int bDefaultNullAccept = -1);
    const char * GetAdditionalDataA(int bDefaultNullAccept = -1, int * pchsize = nullptr) const noexcept;
    _WC_ * GetAdditionalDataW(int bDefaultNullAccept = -1, int * pwsize = nullptr) const noexcept;
protected:
    CMaaString& Sprintf(const char* strFormat, int FormatLen, va_list list, int SrcLine = -1, const char* SrcFile = "unknown") noexcept;
    CMaaString& Sprintf(const char * Format, va_list list, int SrcLine = -1, const char* SrcFile = "unknown") noexcept;
    CMaaString& Sprintf(CMaaString strFormat, va_list list, int SrcLine = -1, const char* SrcFile = "unknown") noexcept;
    CMaaString& Sprintf2(const char* strFormat, int FormatLen, const char* strText, int TextLen, va_list list, int SrcLine = -1, const char* SrcFile = "unknown") noexcept;
    CMaaString& Sprintf2(const char* Format, const char* Text, va_list list, int SrcLine = -1, const char* SrcFile = "unknown") noexcept;
    CMaaString& Sprintf2(CMaaString strFormat, const char *Text, va_list list, int SrcLine = -1, const char* SrcFile = "unknown") noexcept;
    CMaaString& Sprintf2(const char * Format, CMaaString strText, va_list list, int SrcLine = -1, const char* SrcFile = "unknown") noexcept;
    CMaaString& Sprintf2(CMaaString strFormat, CMaaString strText, va_list list, int SrcLine = -1, const char* SrcFile = "unknown") noexcept;

private:
    CMaaString(size_t Length);

    struct CMaaStringImp :
#if MANUAL_STRINGS_REFS
public CMaaManualReference
#else
public CMaaReference
#endif
    {
        CMaaStringImp(const char * pszStr);
        CMaaStringImp(size_t Length, bool bNotZero = false);
        CMaaStringImp(const void * pszStr, size_t Length);
        CMaaStringImp(const void* pszStr, size_t Length, int Flags) = delete; // need "int constexpr Flags" parameter
        CMaaStringImp(const void* pszStr, size_t Length, _e0 Flags) noexcept;
        //CMaaStringImp(const void* pszStr, size_t Length, _e1 Flags) noexcept;
        CMaaStringImp(CMaaStringImp * pRefferedTo, size_t Start, size_t Length) noexcept;
        ~CMaaStringImp() noexcept;
        bool IsSingleOwner() const noexcept
        {
            return
#if MANUAL_STRINGS_REFS
            CMaaManualReference::IsSingleOwner()
#else
            CMaaReference::IsSingleOwner()
#endif
            && !(m_Flags & eHasReffered);
        }
        bool IsRWSingleOwner() const noexcept
        {
            return
#if MANUAL_STRINGS_REFS
                CMaaManualReference::IsSingleOwner()
#else
                CMaaReference::IsSingleOwner()
#endif
                && !(m_Flags & (eHasReffered | eROString));
        }
        bool HasReffered() const noexcept
        {
            return (m_Flags & eHasReffered);
        }
        void ClearHash() noexcept // call after direct string modifications
        {
            m_Flags &= ~ eHashExists;
            //m_pImp->m_Hash = 0;
        }
        bool Is0Behind() const noexcept
        {
            return !(m_Flags & eLengthLimit1) && m_pszStr && m_pszStr[m_iLength] == '\0';
        }
        bool IsWC0Behind() const noexcept
        {
            return !(m_Flags & eLengthLimit2) && m_pszStr && *((m_iLength + (int)sizeof(_WC_) - 1) / (int)sizeof(_WC_) + (const _WC_*)m_pszStr) == 0;
        }
        bool IsWChar0Behind() const noexcept
        {
            if constexpr (sizeof(wchar_t) == 2)
            {
                return !(m_Flags & eLengthLimit2) && m_pszStr && *((m_iLength + (int)sizeof(wchar_t) - 1) / (int)sizeof(wchar_t) + (const wchar_t*)m_pszStr) == 0;
            }
            else
            {
                return !(m_Flags & eLengthLimit4) && m_pszStr && *((m_iLength + (int)sizeof(wchar_t) - 1) / (int)sizeof(wchar_t) + (const wchar_t*)m_pszStr) == 0;
            }
        }
        bool IsWC32_0Behind() const noexcept
        {
            return !(m_Flags & eLengthLimit4) && m_pszStr && *((m_iLength + (int)sizeof(char32_t) - 1) / (int)sizeof(char32_t) + (const char32_t*)m_pszStr) == 0;
        }
        bool IsMemoryString() const noexcept
        {
            return (((m_Flags & eHasReffered) ? m_pRefferedTo->m_Flags : m_Flags) & eMemoryString);
        }
        bool IsROString() const noexcept
        {
            return (((m_Flags & eHasReffered) ? m_pRefferedTo->m_Flags : m_Flags) & eROString);
        }
        bool IsMemoryNotROString() const noexcept // is memory, but not const string
        {
            return (((m_Flags & eHasReffered) ? m_pRefferedTo->m_Flags : m_Flags) & (eMemoryString | eROString)) == eMemoryString;
        }
        
        const char * SetAdditionalData(const char * ptr, int length);
        const char* GetAdditionalData(int* pchsize = nullptr) const noexcept;
        int GetAdditionalLength() const noexcept;

        char * m_pszStr;
        int    m_iLength;  // length of string. real size one byte longer

        enum
        {
            //eDefault    =     0,
            eDontDelete   =     1,
            eHashExists   =     2,
            eHasReffered  =     4,
            eROString     =     8,
            eLengthLimit1 =  0x10,
            eLengthLimit2 =  0x20,
            eLengthLimit4 =  0x40,
            eLengthLimit  = eLengthLimit1 | eLengthLimit2 | eLengthLimit4,
            eMemoryString =  0x80,
            eHasAddData   = 0x100 //,
            //eOftenCompared = 0x200
        };
        int m_Flags; // = 0;

        CMaaStringImp* m_pRefferedTo; // = nullptr; // eHasReffered
        char * m_pszData;// = nullptr; // eHasAddData
        int    m_iDataLength;// = 0;  // length of WCHAR m_pszData string // eHasAddData

#ifdef CMAASTRING_CACHE_HASH
        _dword m_Hash; // uninitialized if (!(m_Flag & 2))
#endif
        ADD_ALLOCATOR(CMaaStringImp)
    };

    friend struct CMaaString::XX;

    CMaaStringImp * m_pImp;

    static void __AssignFast(CMaaStringImp * &a, CMaaStringImp * &b, bool bInc, bool bDel = MANUAL_STRINGS_REFS) noexcept
    {
        //gCMaaSyncAssignLock.Lock();
        if  (b && bInc)
        {
            b->AddRef();
        }
        CMaaStringImp * tmp = a;
        a = b;
        if  (tmp)
        {
            if  (!tmp->Release() && bDel)
            {
                delete tmp;
            }
        }
        //gCMaaSyncAssignLock.UnLock();
    }
public:
static const CMaaString sCMaaStringZ, sCMaaString0, sCMaaString00, sCMaaStringWC0, sCMaaStringWChar0, sCMaaStringWC32_0;
static const CMaaString sCMaaStringEta, sCMaaStringSp, sCMaaString_0, sCMaaString_a, sCMaaString_x, sCMaaStringLf, sCMaaStringCr, sCMaaStringCrLf, sCMaaStringSpSp/*, sCMaaString_Nums[10], sCMaaString_a, sCMaaString_z, sCMaaString_A, sCMaaString_Z*/, sCMaaStringSemicolon, sCMaaStringComma, sCMaaStringDot, sCMaaStringDotDot/*, sCMaaStringEllipse*/, sCMaaStringDoc;
static const CMaaString sCMaaStringJsonVal, sCMaaStringJsonNode, sCMaaStringJsonArray, sCMaaStringJsonName, sCMaaStringJsonValue, sCMaaStringJsonType, sCMaaStringJsonInt, sCMaaStringJsonBool, sCMaaStringJsonFloat, sCMaaStringJsonNull, sCMaaStringJsonString;
};

//------------------------------------------------------------------------------
//inline CMaaString CMaaStringRO(const char* x) noexcept { return CMaaString(x, CMaaString::eROStrlenMemString); }
//------------------------------------------------------------------------------
#define CMaaStringRO(x) CMaaString(x, CMaaString::eROStrlenMemString)
//------------------------------------------------------------------------------
inline CMaaString operator""_s(const char* __str, size_t __len) noexcept { return CMaaString(__str, __len, CMaaString::eROMemString0); }
#define CMaaStringRO_s(x) x##_s
//------------------------------------------------------------------------------

template<> int CMaaString::FindN_<0>(int StartPos, int n, const char** SubStr, const int* SubStrLen, int EndPos, int Flags, const int* Index, int* pIndex) const noexcept;
template<> int CMaaString::FindN_<1>(int StartPos, int n, const char** SubStr, const int* SubStrLen, int EndPos, int Flags, const int* Index, int* pIndex) const noexcept;
template<> int CMaaString::FindN<0>(int StartPos, const char** SubStr, int* SubStrLen, int* pIndex, int EndPos, int Flags) const noexcept;
template<> int CMaaString::FindN<1>(int StartPos, const char** SubStr, int* SubStrLen, int* pIndex, int EndPos, int Flags) const noexcept;

template<> int CMaaString::KMPFindN_<0>(int StartPos, int n, const char** SubStr, const int* SubStrLen, int EndPos, const int** pf, int Flags, const int* Index, int* pIndex) const noexcept;
template<> int CMaaString::KMPFindN_<1>(int StartPos, int n, const char** SubStr, const int* SubStrLen, int EndPos, const int** pf, int Flags, const int* Index, int* pIndex) const noexcept;
template<> int CMaaString::KMPFindN<0>(int StartPos, const char** SubStr, int* SubStrLen, int* pIndex, int EndPos, sKMPFindNCalcData<0>* f, int Flags) const noexcept;
template<> int CMaaString::KMPFindN<1>(int StartPos, const char** SubStr, int* SubStrLen, int* pIndex, int EndPos, sKMPFindNCalcData<1>* f, int Flags) const noexcept;

//extern const CMaaString CMaaStringZ, CMaaString0, CMaaStringWC0, CMaaStringWChar0, CMaaStringWC32_0;
template<class T> const CMaaString & CMaaStr0() noexcept
{
    static const T ch{};
    static const CMaaString s(&ch, (int)sizeof(ch), CMaaString::eROMemString);
    return s;
}
template<> const CMaaString & CMaaStr0<void>() noexcept;
template<class T, T c> const CMaaString& CMaaStrCh() noexcept
{
    static const T ch{c};
    static const CMaaString s(&ch, (int)sizeof(ch), CMaaString::eROMemString);
    return s;
}

inline const CMaaString & CMaaStrCh(char C, bool bInit = false) noexcept
{
    static char ch[2 * 256];
    static CMaaString s[256];
    if (bInit && !ch[2 * 255])
    {
        for (int i = 0; i < 256; i++)
        {
            ch[2 * i] = (char)i;
            //ch[2 * i + 1] = '\0';
            s[i] = CMaaString(&ch[2 * i], 1, CMaaString::eROMemString0);
        }
    }
    return s[(unsigned char)C];
}

/*
#define CMaaString_0 CMaaStrCh('0')
#define CMaaString_1 CMaaStrCh('1')
#define CMaaString_2 CMaaStrCh('2')
#define CMaaString_3 CMaaStrCh('3')
#define CMaaString_4 CMaaStrCh('4')
#define CMaaString_5 CMaaStrCh('5')
#define CMaaString_6 CMaaStrCh('6')
#define CMaaString_7 CMaaStrCh('7')
#define CMaaString_8 CMaaStrCh('8')
#define CMaaString_9 CMaaStrCh('9')
#define CMaaString_a CMaaStrCh('a')
#define CMaaString_z CMaaStrCh('z')
#define CMaaString_A CMaaStrCh('A')
#define CMaaString_Z CMaaStrCh('Z')
*/

////using CMaaStringZ = CMaaStr0<void>();
////using CMaaString0 = CMaaStr0<char>();
////using CMaaStringWC0 = CMaaStr0<_WC_>();
////using CMaaStringWChar0 = CMaaStr0<wchar_t>();
////using CMaaStringWC32_0 = CMaaStr0<char32_t>();
//#define CMaaStringZ CMaaStr0<void>()
//#define CMaaString0 CMaaStr0<char>()
//#define CMaaStringWC0 CMaaStr0<_WC_>()
//#define CMaaStringWChar0 CMaaStr0<wchar_t>()
//#define CMaaStringWC32_0 CMaaStr0<char32_t>()
////#define CMaaStringEta CMaaStrCh<char, '@'>()
////#define CMaaStringSp CMaaStrCh<char, ' '>()
////#define CMaaStringSemicolon CMaaStrCh<char, ';'>()
////#define CMaaStringComma CMaaStrCh<char, ','>()
////#define CMaaStringDot CMaaStrCh<char, '.'>()

#if defined(_WIN32) && defined(_UNICODE)
#define _uc_Utf8ToUnicode_str(x) Utf8ToUnicode(x)
#define _uc_Utf8ToUnicode(x) (_WC_*)(const char *)Utf8ToUnicode(x)
#else
#define _uc_Utf8ToUnicode_str(x) CMaaString(x)
#define _uc_Utf8ToUnicode(x) (const char *)(x)
#endif

#ifdef __unix__
#define CP_ACP 1251
#endif

CMaaString UnicodeToAnsi(const CMaaString &Text, bool RemoveLastNullChar = true, _dword cp = CP_ACP);
CMaaString AnsiToUnicode(const CMaaString &Text, bool AddTerminatingNullChar = false, _dword cp = CP_ACP);

inline CMaaString Utf8ToAnsi(const CMaaString &Text, int bDefaultNullAccept = false) {return UnicodeToAnsi(Utf8ToUnicode(Text, bDefaultNullAccept ? 1 : 0), false);}

#ifdef CMaaStringReplace
CMaaString Export(const void * p, int len);
//static CMaaString DataToString(const void * ptr, int len);
//#define CTOOString CMaaString
#endif
CMaaString Export(const CMaaString &Data);

CMaaString GetLogDatePath(time_t t) noexcept;
CMaaString GetTextDate(time_t t, int mode = 0) noexcept;
CMaaString GetTextTime(time_t t) noexcept;
CMaaString GetTextDateTime(time_t t, int mode = 0) noexcept;
CMaaString GetTextTimeDate(time_t t, int mode = 0) noexcept;
CMaaString GetTextDateOrTimeToday(time_t t, time_t current_time = time(nullptr), int mode = 0) noexcept;
CMaaString GetTextTimeDiff(double x) noexcept;  // x - time difference int secounds, by example diff of GetTickCount()/1000.0
CMaaString GetTextTimeDiff3(double x) noexcept; // x - time difference int secounds, by example diff of GetTickCount()/1000.0
CMaaString GetHumanLikeTextDate(time_t t, int mode /*=0*/, const CMaaString * const Month, const CMaaString &strY /* = " г." */, const CMaaString * const Dow = nullptr) noexcept;
CMaaString CMaaSqlDate(time_t t) noexcept;
time_t ConvertTextDateTime(const CMaaString &Date, const CMaaString &Time, int mode = 0, bool bStrong = false) noexcept;
time_t ConvertTextDateTime(const CMaaString &DateTime, int mode = 0, bool bStrong = false) noexcept;
CMaaString GetDateMailFormat(time_t t = time(nullptr)) noexcept;
CMaaString GetDateTimeYandexdFormat(time_t t = time(nullptr));
CMaaString GetDateTimeYandexdFormatForMWS(time_t t = time(nullptr));
CMaaString GetDateTimeNalogFormat(time_t t = time(nullptr));
CMaaString GetW3CDateTime(time_t t = time(nullptr)); // for sitemaps
CMaaString GetFromToMailText(const CMaaString &TextUtf8UserName, const CMaaString &EMail);
CMaaString MakeMailSubject(const CMaaString &Utf8Text);
CMaaString GetMailSubject(const CMaaString &Subject);
CMaaString MakeMailText(CMaaString Text, int LineLen = 72);
CMaaString GetDateApacheFormat(time_t t = time(nullptr)) noexcept;
bool ConvertFromWebDateTime(const CMaaString &s, time_t *t) noexcept;
bool ConvertFromApacheDateTime(const CMaaString &s, time_t *t) noexcept;
CMaaString GetDateWebFormat(time_t t) noexcept;
bool CMaaIsValidDate(const CMaaString &Date, const CMaaString &Time = CMaaStringZ, int mode = -1); // -1 - both 0,2 modes, '/','S' are not
bool CMaaIsValidDateTime(CMaaString dt, int mode = -1);

int To0F(char c) noexcept;
_qword GetHexNum(const char * ptr, int len = -1, int *pStopPos = nullptr, CMaaString StopChars = CMaaString0, bool * pbError = nullptr) noexcept;
_qword GetHexNum(const char * ptr, int len /*= -1*/, _qword ErrorRetValue /*= -1*/, int *pStopPos = nullptr, CMaaString StopChars = CMaaString0, bool * pbError = nullptr) noexcept;
CMaaString UrlWithMixedUtf8ToUtf8(const CMaaString &Text); // noexept // exceptions are suppressed
CMaaString UrlWithMixedUtf8ToAscii(const CMaaString &Text);

CMaaString GetTextSizeKBMBGB(_qword q, const char * m[] = nullptr) noexcept;
// const char * mr[6] = {"Байт", "КБ", "МБ", "ГБ", "ТБ", nullptr}; // default
// const char * me[6] = {"Bytes", "KB", "MB", "GB", "TB", nullptr};
// CMaaString sz = GetTextSizeKBMBGB(q, me);

bool CMaaGetStartTime(time_t &t, char c) noexcept; // c is 'y' 'm' 'd' 'h', '.'
bool CMaaGetNextTime(time_t &t, char c) noexcept; // c is 'y' 'm' 'd' 'h', '.'
time_t CMaaMkTime(tm * tt1) noexcept;
time_t CMaaMkTimeOrig(tm * tt1) noexcept;

//<div class="_additional_info_" align="right" style="font-family: 'Times New Roman,font1'; font-size: 14px; text-decoration: none;">
//Copyright &copy; 2011  <a href="http://www.maasoftware.ru/" style="font-family: 'Times New Roman,font1'; font-size: 14px; font-weight: bold; color: #FF4040; text-decoration: none;" target="_top">MaaSoftware</a>&nbsp;</div>

//CMaaString GetCopyrightText(int StartYear = 0, const char * format = "Copyright &copy; %1 MaaSoftware", time_t t = time(nullptr));
CMaaString GetCopyrightText(int StartYear = 0, const char * format = "Copyright &copy; %1 "
"<a href=\"http://maasoft.ru/\" style=\"font-family: 'Times New Roman,font1'; font-size: 14px; font-weight: bold; color: #FF4040; text-decoration: none;\" target=\"_top\">"
"MaaSoftware"
"</a>"
, time_t t = time(nullptr)) noexcept;
CMaaString CRAM_MD5_Auth(const CMaaString &Base64Request, const CMaaString &email, CMaaString pass);

class CMaaConcatString
{
    char * m_ptr;
    CMaaPtr<char> m_Buffer;
    size_t m_StringLength, m_BufferSize;
    bool m_bIsValid, m_bCountMode;
public:
#if 1
    CMaaConcatString(int ApproxLen = 1024, int xThrow = 0, bool bCountMode = false)
    :   m_Buffer(bCountMode ? 0 : ApproxLen >= 0 ? ApproxLen : 1024, xThrow),
        m_StringLength(0),
        m_bCountMode(bCountMode)
    {
        m_bIsValid = !bCountMode && m_Buffer.IsValid();
        m_ptr = m_Buffer;
        m_BufferSize = m_Buffer.Size();
    }
#else
    CMaaConcatString(int ApproxLen = 1024, int xThrow = 0)
    :   m_Buffer(ApproxLen >= 0 ? ApproxLen : 1024, xThrow),
        m_StringLength(0),
        m_bCountMode(false)
    {
        m_bIsValid = !false && m_Buffer.IsValid();
        m_ptr = m_Buffer;
        m_BufferSize = m_Buffer.Size();
    }
#endif
    CMaaConcatString(char * pExtBuffer, size_t ExtBufferSize, int ApproxLen = 1024, int xThrow = 0, bool bCountMode = false)
    :   m_ptr(pExtBuffer),
        m_Buffer(!bCountMode && ApproxLen >= 0 && ((size_t)ApproxLen > ExtBufferSize || !pExtBuffer) ? ApproxLen : 0, xThrow),
        m_StringLength(0),
        m_bCountMode(bCountMode)
    {
        if (bCountMode)
        {
            m_bIsValid = false;
            m_BufferSize = 0;
        }
        else if (ApproxLen >= 0 && ((size_t)ApproxLen > ExtBufferSize || !pExtBuffer))
        {
            m_bIsValid = m_Buffer.IsValid();
            m_ptr = m_Buffer;
            m_BufferSize = m_Buffer.Size();
        }
        else
        {
            m_bIsValid = pExtBuffer != nullptr;
            if (!pExtBuffer && ExtBufferSize)
            {
                if (xThrow > 0)
                {
                    CMaaPtr_THROW1_BAD_IMP;
                }
                else if (!xThrow)
                {
                    CMaaPtr_THROW_BAD_IMP;
                }
                m_BufferSize = 0;
            }
            else
            {
                m_BufferSize = ExtBufferSize;
            }
        }
    }
    size_t GetLength() const noexcept
    {
        return m_StringLength;
    }
    size_t Length() const noexcept
    {
        return GetLength();
    }
    bool IsValid() const noexcept
    {
        return m_bIsValid || m_bCountMode;
    }
    void Empty(bool bResideBuffer = false) noexcept
    {
        if (bResideBuffer)
        {
            CMaaPtr<char> Buffer(0, m_Buffer.Get_xThrow());
            m_Buffer.Swap(Buffer);
            m_bIsValid = !m_bCountMode && m_Buffer.IsValid();
            m_ptr = m_Buffer;
            m_BufferSize = 0;
        }
        m_StringLength = 0;
    }
    bool SetNewLengthValue(size_t Length) noexcept
    {
        if  (m_bIsValid && Length <= m_StringLength)
        {
            m_StringLength = Length;
            return true;
        }
        /*
          if   (m_Buffer.GetThrowParameter() > 0)
          {
               CMaaPtr_THROW1;
          }
          else
          {
               CMaaPtr_THROW;
          }
        */
        return false;
    }
    void operator += (const CMaaString &str)
    {
        Add(str, str.Length());
    }
    /*
    void operator += (CMaaString str)
    {
        Add(str, str.Length());
    }
    */
    void operator += (const char* str)
    {
        Add(str, (int)strlen(str));
    }
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    void operator += (const char8_t* str)
    {
        Add(str, (int)strlen((const char*)str));
    }
#endif
    void operator += (char c)
    {
        Add(&c, 1);
    }
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    void operator += (char8_t c)
    {
        Add(&c, 1);
    }
#endif
    void Add(const char * str, int len)
    {
        if  (len <= 0)
        {
            return;
        }
        if  (m_bIsValid)
        {
            if  (m_BufferSize < m_StringLength + (size_t)len)
            {
                size_t NewLen = 2 * ((size_t)m_StringLength + (size_t)len);
                if  (NewLen > 0x7fffffff && (size_t)len + (size_t)m_StringLength < 0x7fffffff)
                {
                    NewLen = 0x7fffffff;
                }
                NewLen = NewLen < 32 ? 32 : NewLen;
                if  (NewLen < m_StringLength + len || NewLen < m_StringLength || NewLen < (size_t)len)
                {
                    m_bIsValid = false;
                }
                else
                {
                    CMaaPtr<char> Buffer(NewLen, m_Buffer.Get_xThrow());
                    if  ((m_bIsValid = Buffer.IsValid()))
                    {
                        memcpy((char *)Buffer, m_ptr, m_StringLength);
                        m_ptr = Buffer;
                        m_BufferSize = Buffer.Size();
                        m_Buffer.Swap(Buffer);
                    }
                }
            }
            if  (m_bIsValid)
            {
                memmove(m_StringLength + m_ptr, str, len);
            }
        }
        m_StringLength += len;
    }
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    void Add(const char8_t* str, int len)
    {
        Add((const char*)str, len);
    }
#endif
    operator CMaaString() const
    {
        /*
          if   (m_bIsValid && m_StringLength < (size_t)(((unsigned)-1) >> 1))
          {
               CMaaString str((char *)(CMaaPtr<char> &)m_Buffer, (int)m_StringLength);
               if   ((size_t)str.Length() != m_StringLength && m_Buffer.GetThrowParameter() >= 0)
               {
                    if   (m_Buffer.GetThrowParameter() > 0)
                    {
                         CMaaPtr_THROW1;
                    }
                    else
                    {
                         CMaaPtr_THROW;
                    }
               }
               return str;
          }
          */
        return GetMemoryString().NewCopy();
    }
    CMaaString GetMemoryString() const
    {
        if  (m_bIsValid && m_StringLength < (size_t)(((unsigned)-1) >> 1))
        {
            CMaaString str(m_ptr, (int)m_StringLength, CMaaString::eMemString); // RO? opt
            if  ((size_t)str.Length() != m_StringLength && m_Buffer.GetThrowParameter() >= 0)
            {
                if  (m_Buffer.GetThrowParameter() > 0)
                {
                    CMaaPtr_THROW1;
                }
                else
                {
                    CMaaPtr_THROW;
                }
            }
            return str;
        }
        return CMaaStringZ;
    }
};

template <> _dword CMaaHashFunction<CMaaString>(const CMaaString & k) noexcept;
inline _dword CMaaHashFunction(const CMaaString & k) noexcept
{
    return k.Hash();
}


//------------------------------------------------------------------------------
// class CMaaString
//------------------------------------------------------------------------------
class CMaaStringG
{
    CMaaString s;
    struct lk
    {
        lk() noexcept
        {
#ifndef TOOLSLIB_SINGLE_THREAD
            gCMaaSyncAssignLockUpperLock();
#endif
        }
        ~lk() noexcept
        {
#ifndef TOOLSLIB_SINGLE_THREAD
            gCMaaSyncAssignLockUpperUnLock();
#endif
        }
    };
    void Lock() noexcept
    {
#ifndef TOOLSLIB_SINGLE_THREAD
        gCMaaSyncAssignLockUpperLock();
#endif
    }
    void UnLock() noexcept
    {
#ifndef TOOLSLIB_SINGLE_THREAD
        gCMaaSyncAssignLockUpperUnLock();
#endif
    }
public:
    CMaaStringG() noexcept
    {
    }
    CMaaStringG(const char * pszStr)
    :   s(pszStr)
    {
    }
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaStringG(const char8_t * pszStr)
        : s(pszStr)
    {
    }
#endif
    CMaaStringG(const void * pMem, size_t Len)
    :   s(pMem, Len)
    {
    }
    CMaaStringG(const CMaaString & That) noexcept
    :   s(That)
    {
    }
    CMaaString operator=(const CMaaString & That) noexcept
    {
        lk l;
        s = That;
        return s;
    }
    CMaaString operator=(const char * pszTxt)
    {
        lk l;
        s = pszTxt;
        return s;
    }
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaString operator=(const char8_t * pszTxt)
    {
        lk l;
        s = pszTxt;
        return s;
    }
#endif
    CMaaString operator=(const CMaaString & That) const noexcept = delete;
    CMaaString operator=(const char * pszTxt) const = delete;
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    CMaaString operator=(const char8_t * pszTxt) const = delete;
#endif

    ~CMaaStringG() noexcept
    {
    }

    bool operator == (const char * str) const noexcept
    {
        lk l;
        return s == str;
    }
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    bool operator == (const char8_t * str) const noexcept
    {
        lk l;
        return s == str;
    }
#endif
    bool operator == (const CMaaString & That) const noexcept
    {
        lk l;
        return s == That;
    }
    bool operator != (const char * str) const noexcept
    {
        lk l;
        return s != str;
    }
#ifdef TOOLSLIB_CHAR8T_SUPPORT
    bool operator != (const char8_t * str) const noexcept
    {
        lk l;
        return s != str;
    }
#endif
    bool operator != (const CMaaString & That) const noexcept
    {
        lk l;
        return s != That;
    }
    bool operator < (const CMaaString & That) const noexcept
    {
        lk l;
        return s < That;
    }
    bool operator > (const CMaaString & That) const noexcept
    {
        lk l;
        return s > That;
    }
    operator CMaaString () noexcept
    {
        lk l;
        return s;
    }
    void Format2(const char * format, CMaaString text, ...) noexcept
    {
        CMaaString txt;

        va_list list;
        va_start(list, text);
        txt._FormatV2(-1, "unknown", format, text, list);
        va_end(list);

        *this = txt;
    }
    void Empty() noexcept
    {
        lk l;
        s.Empty(); // *this = CMaaString{};
    }
};

class ToolsExport CMaaStringsContinedBuffer
{
protected:
    int m_BufferSize, m_Start = 0;
    int m_PaddingBytes;
    CMaaString m_CurrentBuffer;
public:
    CMaaStringsContinedBuffer(int BufferSize = 2 * 1024 * 1024, int PaddingBytes = 8) noexcept;
    ~CMaaStringsContinedBuffer() = default;
    CMaaStringsContinedBuffer(const CMaaStringsContinedBuffer &) = delete;
    CMaaStringsContinedBuffer & operator = (const CMaaStringsContinedBuffer &) = delete;
    CMaaString Put(CMaaString s, bool bMemoryString = false);
    void Reset() noexcept;
};

//------------------------------------------------------------------------------
CMaaString SummToTextSumm_rub(CMaaString input, bool bFullText, CMaaString *pSumm, CMaaString *pSummSp, CMaaString *pSummComma);
CMaaString SummToTextSumm_usd(CMaaString input, bool bFullText, CMaaString *pSumm, CMaaString *pSummSp, CMaaString *pSummComma);
CMaaString SummToTextSumm(CMaaString input, int lang = 0, CMaaString *pSumm = nullptr, CMaaString *pSummSp = nullptr, CMaaString *pSummComma = nullptr);
CMaaString NormalizeSummKIB(int len, CMaaString input);
CMaaString NormalizeSummSpComma(CMaaString input);
//------------------------------------------------------------------------------

//-----------------------------------------------------------------------------------------------
template<class T> int SignedIntegerToString_d(T x, char* Buffer, char mode = 0, int width = -1, char z = 0) noexcept //, int prefix = 0)
{
    const bool mode_s = (mode == '+' || mode == ('+' ^ ','));
    const bool mode_c = (mode == ',' || mode == ('+' ^ ','));
    //prefix = prefix ? 2 : 0;
    //prefix = 0;
    if (width < 0)
    {
        z = 0;
    }
    int width2 = width >= 0 ? width : -width;
    int Len;
    char s;
    if (!x)
    {
        if (z == '0' && width2)
        {
            memset(Buffer, '0', width2);
            Buffer[Len = width2] = 0;
            return Len;
        }
        else
        {
            Buffer[0] = '0';
            Len = 1;
            s = 0;
            //prefix = 0;
        }
    }
    else
    {
        s = x < 0 ? '-' : mode_s ? '+' : 0;
        char* p = Buffer;
        const int d10 = 10;
        int nDigits = 0;
        for (x = x > 0 ? -x : x; x; x /= d10)
        {
            if (mode_c)
            {
                if (nDigits && !(nDigits % 3))
                {
                    *p++ = ',';
                }
                nDigits++;
            }
            *p++ = '0' - (char)(x % d10);
        }
        Len = (int)(p - Buffer);
        if (z != '0' || ((s /* || prefix*/) && width > 0 && width <= Len + (s ? 1 : 0) /* +prefix */))
        {
            /*
            if (prefix)
            {
                *p++ = 'x';
                *p++ = '0';
            }
            */
            if (s)
            {
                *p++ = s;
            }
            //s = prefix = 0;
            s = 0;
            Len = (int)(p - Buffer);
        }
        else if (width <= 0)
        {
            /*
            if (prefix)
            {
                *p++ = 'x';
                *p++ = '0';
            }
            */
            if (s)
            {
                *p++ = s;
                // s = 0;
            }
            Len = (int)(p - Buffer);
        }
        for (int i = 0; i < Len / 2; i++)
        {
            char c = Buffer[i];
            Buffer[i] = Buffer[Len - 1 - i];
            Buffer[Len - 1 - i] = c;
        }
    }
    if (width < 0 && width2 > Len)
    {
        memset(Buffer + Len, ' ', width2 - Len);
        Len = width2;
    }
    else if (width > Len + (s ? 1 : 0) /* +prefix */)
    {
        memmove(Buffer + width - Len, Buffer, Len);
        memset(Buffer, z == '0' ? '0' : ' ', width - Len);
        if (s)
        {
            Buffer[0] = s;
            /*
            if (prefix)
            {
                Buffer[1] = '0';
                Buffer[2] = 'x';
            }
            */
        }
        /*
        else if (prefix)
        {
            Buffer[0] = '0';
            Buffer[1] = 'x';
        }
        */
        Len = width;
    }
    Buffer[Len] = 0;
    return Len;
}
//-----------------------------------------------------------------------------------------------
template<class T> int UnsignedIntegerToString_d(T x, char* Buffer, char mode = 0, int width = -1, char z = 0) noexcept //, int prefix = 0)
{
    const bool mode_s = (mode == '+' || mode == ('+' ^ ','));
    const bool mode_c = (mode == ',' || mode == ('+' ^ ','));
    //prefix = prefix ? 2 : 0;
    //prefix = 0;
    if (width < 0)
    {
        z = 0;
    }
    int width2 = width >= 0 ? width : -width;
    int Len;
    char s;
    if (!x)
    {
        if (z == '0' && width2)
        {
            memset(Buffer, '0', width2);
            Buffer[Len = width2] = 0;
            return Len;
        }
        else
        {
            Buffer[0] = '0';
            Len = 1;
            s = 0;
            //prefix = 0;
        }
    }
    else
    {
        s = /*x < 0 ? '-' :*/ mode_s ? '+' : 0;
        char* p = Buffer;
        const unsigned int d10 = 10;
        int nDigits = 0;
        for (; x; x /= d10)
        {
            if (mode_c)
            {
                if (nDigits && !(nDigits % 3))
                {
                    *p++ = ',';
                }
                nDigits++;
            }
            *p++ = '0' + (char)(x % d10);
        }
        Len = (int)(p - Buffer);
        if (z != '0' || ((s /* || prefix*/) && width > 0 && width <= Len + (s ? 1 : 0) /* +prefix */))
        {
            /*
            if (prefix)
            {
                *p++ = 'x';
                *p++ = '0';
            }
            */
            if (s)
            {
                *p++ = s;
            }
            //s = prefix = 0;
            s = 0;
            Len = (int)(p - Buffer);
        }
        else if (width <= 0)
        {
            /*
            if (prefix)
            {
                *p++ = 'x';
                *p++ = '0';
            }
            */
            if (s)
            {
                *p++ = s;
                // s = 0;
            }
            Len = (int)(p - Buffer);
        }
        for (int i = 0; i < Len / 2; i++)
        {
            char c = Buffer[i];
            Buffer[i] = Buffer[Len - 1 - i];
            Buffer[Len - 1 - i] = c;
        }
    }
    if (width < 0 && width2 > Len)
    {
        memset(Buffer + Len, ' ', width2 - Len);
        Len = width2;
    }
    else if (width > Len + (s ? 1 : 0) /* +prefix */)
    {
        memmove(Buffer + width - Len, Buffer, Len);
        memset(Buffer, z == '0' ? '0' : ' ', width - Len);
        if (s)
        {
            Buffer[0] = s;
            /*
            if (prefix)
            {
                Buffer[1] = '0';
                Buffer[2] = 'x';
            }
            */
        }
        /*
        else if (prefix)
        {
            Buffer[0] = '0';
            Buffer[1] = 'x';
        }
        */
        Len = width;
    }
    Buffer[Len] = 0;
    return Len;
}
//-----------------------------------------------------------------------------------------------
template<class T> int UnsignedIntegerToString_x(T x, char* Buffer, int width = -1, char mode = 'x', char z = 0, char prefix = 0) noexcept
{
    mode = mode == 'X' ? 'A' - 10 : 'a' - 10;
    int width2 = width >= 0 ? width : -width;
    if (width < 0)
    {
        z = 0;
    }
    int Len;
    if (!x)
    {
        Buffer[0] = '0';
        Len = 1;
    }
    else
    {
        char* p = Buffer;
        int nDigits = 0;
        for (; x; x >>= 4)
        {
            char c = (char)(x & 0x0f);
            c = c < 10 ? c + '0' : c + mode;
            *p++ = c;
        }
        Len = (int)(p - Buffer);
        {
            if (prefix)
            {
                *p++ = prefix == 'X' ? 'X' : 'x';
                *p++ = '0';
                Len += 2;
            }
        }
        for (int i = 0; i < Len / 2; i++)
        {
            char c = Buffer[i];
            Buffer[i] = Buffer[Len - 1 - i];
            Buffer[Len - 1 - i] = c;
        }
    }
    if (width < 0 && width2 > Len)
    {
        memset(Buffer + Len, ' ', width2 - Len);
        Len = width2;
    }
    else if (width > Len)
    {
        memmove(Buffer + width - Len, Buffer, Len);
        if (z == '0')
        {
            memset(Buffer, '0', width - Len + (prefix ? 2 : 0));
            if (prefix)
            {
                Buffer[0] = '0';
                Buffer[1] = prefix == 'X' ? 'X' : 'x';
            }
        }
        else
        {
            memset(Buffer, ' ', width - Len);
        }
        Len = width;
    }
    Buffer[Len] = 0;
    return Len;
}
//-----------------------------------------------------------------------------------------------
template<class T> int UnsignedIntegerToString_o(T x, char* Buffer, int width = -1, char z = 0, char prefix = 0) noexcept
{
    //mode = mode == 'X' ? 'A' - 10 : 'a' - 10;
    int width2 = width >= 0 ? width : -width;
    if (width < 0)
    {
        z = 0;
    }
    int Len;
    if (!x)
    {
        Buffer[0] = '0';
        Len = 1;
    }
    else
    {
        char* p = Buffer;
        int nDigits = 0;
        for (; x; x >>= 3)
        {
            *p++ = (char)(x & 0x07) + '0';
        }
        Len = (int)(p - Buffer);
        {
            if (prefix)
            {
                *p++ = '0';
                Len++;
            }
        }
        for (int i = 0; i < Len / 2; i++)
        {
            char c = Buffer[i];
            Buffer[i] = Buffer[Len - 1 - i];
            Buffer[Len - 1 - i] = c;
        }
    }
    if (width < 0 && width2 > Len)
    {
        memset(Buffer + Len, ' ', width2 - Len);
        Len = width2;
    }
    else if (width > Len)
    {
        memmove(Buffer + width - Len, Buffer, Len);
        memset(Buffer, z == '0' ? '0' : ' ', width - Len);
        Len = width;
    }
    Buffer[Len] = 0;
    return Len;
}
//-----------------------------------------------------------------------------------------------
template<class T> int UnsignedIntegerToString_p(T x, char* Buffer, int width = -1, char mode = 'x', char prefix = 0) noexcept
{
    mode = mode == 'X' ? 'A' - 10 : 'a' - 10;
    int width2 = width >= 0 ? width : -width;
    int Len;
    if (prefix)
    {
        Buffer[0] = '0';
        Buffer[1] = prefix == 'X' ? 'X' : 'x';
        Len = 2;
    }
    else
    {
        Len = 0;
    }
    {
        char* p = Buffer + Len + 2 * sizeof(T);
        for (; p > Buffer + Len; x >>= 4)
        {
            char c = (char)(x & 0x0f);
            c = c < 10 ? c + '0' : c + mode;
            *(--p) = c;
        }
        Len += (int)(2 * sizeof(T));
    }
    if (width < 0 && width2 > Len)
    {
        memset(Buffer + Len, ' ', width2 - Len);
        Len = width2;
    }
    else if (width > Len)
    {
        memmove(Buffer + width - Len, Buffer, Len);
        memset(Buffer, ' ', width - Len);
        Len = width;
    }
    Buffer[Len] = 0;
    return Len;
}
//-----------------------------------------------------------------------------------------------

#ifdef __linux__
int IsBadReadPtr(const void *ptr, size_t s) noexcept;
int IsBadWritePtr(void *ptr, size_t s) noexcept;
int IsBadStringPtrA(const char *ptr, size_t s) noexcept;
int IsBadStringPtrW(const wchar_t *ptr, size_t s) noexcept;
int IsBadStringPtrWC2(const _WC_ *ptr, size_t s) noexcept;
#endif
#if defined(_WIN32) || defined(__linux__)
bool __is_bad_read_ptr(const void *ptr, int len, bool bAccert = true, const char* SrcFile = "unknown", int SrcLine = -1) noexcept;
bool __is_bad_write_ptr(void *ptr, int len, bool bAccert = true, const char* SrcFile = "unknown", int SrcLine = -1) noexcept;
bool __is_bad_string_ptr(const char *ptr, int maxlen, bool bAccert = true, const char* SrcFile = "unknown", int SrcLine = -1) noexcept;
bool __is_bad_wstring_ptr(const wchar_t *ptr, int maxlen, bool bAccert = true, const char* SrcFile = "unknown", int SrcLine = -1) noexcept;
bool __is_bad_wc2string_ptr(const _WC_ *ptr, int maxlen, bool bAccert = true, const char* SrcFile = "unknown", int SrcLine = -1) noexcept;
#endif


class CMaaTLGlobalStrings
{
public:
    enum e
    {
        e_encodeURIComponent_GoodCharacters = 0,
        e_EncodeAttr_r,
        e_DecodeAttr_r = e_EncodeAttr_r + 7,
        e_ae_Operators = e_DecodeAttr_r + 6,
        e_ae__szFunctions1 = e_ae_Operators + 25,
        e_ae__szFunctions2 = e_ae__szFunctions1 + 9,
        e_ae__modificators = e_ae__szFunctions2 + 4,
        e_ae__Modificators_1st_chars_1 = e_ae__modificators + 24,
        e_ae__Modificators_1st_chars_2,
        e_ae__ch_modificators,
        e_ae__sp_ch_modificators,
        e_RussianAlphabet2,
        e_FullRussianAlphabet2,
        e_LatRus1251Lower,
        e_LatRus1251Upper,
        e_Cr,
        e_Lf,
        e_CrLf,
        e_LfCr,
        e_max
    };
    CMaaTLGlobalStrings() noexcept;
    CMaaString & Get(int _e) noexcept;

protected:
    CMaaString m[e_max + 1];
};

//int TestRepace();

const CMaaString & CMaaTLGlobalString(CMaaTLGlobalStrings::e _e, bool bInit = false) noexcept;
const CMaaString & CMaaTLGlobalString(int _e, bool bInit = false) noexcept;

//------------------------------------------------------------------------------
//
// Documentation.
//
// CMaaString & CMaaString::Format(const char * format, ...) uses
// standart type of format string exept of outputting strings (%s) and chars (%c).
// For thease types you can use parameters like '%-20.10s" where
// '-' - 'left alignment' ( alignment is right by default )
// 20  - width for restricting output
// 10  - means 'does not output more than 10 chars from string'
//

//CMaa_CLASSDEF ( CMaaString )

//==============================================================================
